Sie müssen überprüfen, ob eine Tabelle vorhanden ist, bevor Sie einen Datensatz löschen

7

Ich habe die folgende SQL-Abfrage in meiner Ruby-App:

sql = "DELETE FROM `#{database}`.`table1` WHERE `same_id` = #{some_id};"

Das Problem ist, dass es in seltenen Fällen table1möglicherweise nicht existiert. Ich muss diese Abfrage so erstellen, dass SQL auch dann keinen Fehler auslöst, wenn die Tabelle nicht vorhanden ist.

Wie kann ich das umsetzen?

GTE
quelle

Antworten:

8

Sie sollten wahrscheinlich eine gespeicherte Prozedur verwenden, um dies zu tun:

DELIMITER $$ 

DROP PROCEDURE IF EXISTS `test`.`DeleteByID` $$ 
CREATE PROCEDURE `test`.`DeleteByID` (db VARCHAR(64),tb VARCHAR(64),id_to_delete INT) 
BEGIN 
    DECLARE FoundCount INT;

    SELECT COUNT(1) INTO FoundCount
    FROM information_schema.tables
    WHERE table_schema = db
    AND table_name = tb;

    IF FoundCount = 1 THEN
        SET @sql = CONCAT('DELETE FROM ',db,'.',tb,' WHERE id=',id_to_delete); 
        PREPARE stmt FROM @sql; 
        EXECUTE stmt; 
        DEALLOCATE PREPARE stmt;
    END IF;

END $$ 

DELIMITER ; 

Alles, was Sie in Ihrem Code tun, ist die gespeicherte Prozedur aufzurufen. So löschen Sie beispielsweise die ID 128 aus drupaldb.comments:

CALL test.DeleteByID('drupaldb','comments',128);

Versuche es !!!

Eine andere Variante wäre, die Abfrage direkt zu formen:

set @given_db = 'drupaldb';
set @given_tb = 'comments';
set @given_id = 128;
set @good_sql = CONCAT('DELETE FROM ',@given_db,'.',@given_tb,' WHERE id=',@given_id);
set @evil_sql = 'SELECT 1';
SELECT IF(table_exists=1,@good_sql,@evil_sql) INTO @DeleteSQL
FROM
(
    SELECT COUNT(1) table_exists
    FROM information_schema.tables 
    WHERE table_schema=@given_db
    AND table_name=@given_tb
) A;
PREPARE stmt FROM @DeleteSQL; EXECUTE stmt; DEALLOCATE PREPARE stmt;

HINWEIS: Wenn die Tabelle nicht vorhanden ist, wird die Abfrage ausgeführt SELECT 1.

Hier ist ein Beispiel, das zeigt, ob die Tabelle nicht vorhanden ist:

mysql>     set @given_db = 'drupaldb';
Query OK, 0 rows affected (0.00 sec)

mysql>     set @given_tb = 'comments';
Query OK, 0 rows affected (0.00 sec)

mysql>     set @given_id = 128;
Query OK, 0 rows affected (0.00 sec)

mysql>     set @good_sql = CONCAT('DELETE FROM ',@given_db,'.',@given_tb,' WHERE id=',@given_id);
Query OK, 0 rows affected (0.00 sec)

mysql>     set @evil_sql = 'SELECT 1';
Query OK, 0 rows affected (0.00 sec)

mysql>     SELECT IF(table_exists=1,@good_sql,@evil_sql) INTO @DeleteSQL
    ->     FROM
    ->     (
    ->         SELECT COUNT(1) table_exists
    ->         FROM information_schema.tables
    ->         WHERE table_schema=@given_db
    ->         AND table_name=@given_tb
    ->     ) A;
Query OK, 1 row affected (0.00 sec)

mysql>     PREPARE stmt FROM @DeleteSQL; EXECUTE stmt; DEALLOCATE PREPARE stmt;
Query OK, 0 rows affected (0.00 sec)
Statement prepared

+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

mysql>

Hier ist ein Beispiel, in dem die Tabelle vorhanden ist:

mysql> select count(1) from mysql.user where user='rolando';
+----------+
| count(1) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

mysql> grant all on *.* to 'rolando'@'127.0.0.1';
Query OK, 0 rows affected (0.00 sec)

mysql> select count(1) from mysql.user where user='rolando';
+----------+
| count(1) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

mysql> set @given_db = 'mysql';
Query OK, 0 rows affected (0.00 sec)

mysql> set @given_tb = 'user';
Query OK, 0 rows affected (0.00 sec)

mysql> set @given_id = 'rolando';
Query OK, 0 rows affected (0.00 sec)

mysql> set @good_sql = CONCAT('DELETE FROM ',@given_db,'.',@given_tb,' WHERE user=''',@given_id,'''');
Query OK, 0 rows affected (0.00 sec)

mysql> set @evil_sql = 'SELECT 1';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT IF(table_exists=1,@good_sql,@evil_sql) INTO @DeleteSQL
    -> FROM
    -> (
    ->     SELECT COUNT(1) table_exists
    ->     FROM information_schema.tables
    ->     WHERE table_schema=@given_db
    ->     AND table_name=@given_tb
    -> ) A;
Query OK, 1 row affected (0.00 sec)

mysql> PREPARE stmt FROM @DeleteSQL; EXECUTE stmt; DEALLOCATE PREPARE stmt;
Query OK, 0 rows affected (0.02 sec)
Statement prepared

Query OK, 1 row affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

mysql> select count(1) from mysql.user where user='rolando';
+----------+
| count(1) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

mysql>
RolandoMySQLDBA
quelle
Ich würde vorschlagen, eine Version hinzuzufügen, in der dies funktioniert, da die CONCAT-Funktion nur für 2012 + ... verfügbar ist
fireshark519
@ fireshark519 MySQL 5.0 hat CONCAT()eine Funktion. Möglicherweise tritt in StackOverflow ( stackoverflow.com/questions/4148149/… ) dasselbe Problem auf, das im November 2010 aufgetreten ist . Eine andere Website gibt an, dass CONCAT()auf MySQL 3.23 ( techonthenet.com/mysql/functions/concat.php ) zurückgegriffen wird
RolandoMySQLDBA
4

Gemäß der Dokumentation behandelt die Option IGNORE Fehler als Warnungen.

DELETE IGNORE ...

Das Schlüsselwort IGNORE bewirkt, dass MySQL alle Fehler beim Löschen von Zeilen ignoriert. (Während der Analysephase aufgetretene Fehler werden auf die übliche Weise verarbeitet.) Fehler, die aufgrund der Verwendung von IGNORE ignoriert werden, werden als Warnungen zurückgegeben.

Ich bin nicht sicher, ob MySQL eine nicht vorhandene Tabelle als "Fehler in der Analysephase" behandeln würde. In diesem Fall können Sie das Informationsschema nach einem Tabellennamen abfragen . Etwas in diese Richtung sollte funktionieren.

SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'your database name'
AND table_name = 'your table name';

Temporäre Tabellen werden in den Ansichten information_schema nicht angezeigt. Wenn Ihre Tabelle eine temporäre Tabelle ist, liegt ein Fehler in Ihrem Code vor. Sie lassen den Tisch fallen, bevor Sie damit fertig sind.

Mike Sherrill 'Cat Recall'
quelle
Vielen Dank für die schnelle Antwort. Ich habe versucht, IGNORE ohne Erfolg zu verwenden. Ich denke, die nicht vorhandene Tabelle wird während der Analysephase gefunden. Wie würde ich SELECT und DELETE als eine Abfrage erstellen? Soweit ich weiß, kann ich IF-Anweisungen nicht verwenden, da MySQL sie nur als gespeicherte Prozeduren unterstützt.
GTE
2
Sie können es nicht in einer einzigen Anweisung tun. Warum möchten Sie den Fehler in Ruby nicht behandeln? (Es ist nicht sinnvoll, Fehler in Ruby nicht zu behandeln.)
Mike Sherrill 'Cat Recall'
Tipp zum Datenbanknamen: Sie können einfach DATABASE () verwenden, um das aktuell ausgewählte Schema zu verwenden.
Garr Godfrey
DELETE IGNOREDer Fehler in einer nicht vorhandenen Tabelle wird nicht stummgeschaltet. #1146 - Table 'nonexistent_tablename' doesn't exist
Mickmackusa
3

Ich würde testen, ob die Tabelle zuerst vorhanden ist, indem ich eine Abfrage wie z.

SHOW TABLES LIKE 'table1';

Führen Sie dann das Löschen basierend auf den Ergebnissen dieser Abfrage durch. Wenn die Tabelle vorhanden ist, wird ein einzelner Datensatz mit dem Tabellennamen zurückgegeben.

Dave Rix
quelle