Cần kiểm tra nếu bảng tồn tại trước khi xóa bản ghi


7

Tôi có truy vấn SQL sau trong ứng dụng Ruby của mình:

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

Vấn đề là trong những trường hợp hiếm hoi table1có thể không tồn tại. Tôi cần xây dựng truy vấn này để ngay cả khi bảng không tồn tại, SQL cũng không gây ra lỗi.

Làm thế nào tôi có thể thực hiện điều này?

Câu trả lời:


8

Bạn có thể nên sử dụng một thủ tục được lưu trữ để làm điều này:

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 ; 

Tất cả những gì bạn làm trong mã của bạn là gọi thủ tục được lưu trữ. Ví dụ: để xóa ID 128 khỏi drupaldb.comments:

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

Hãy thử một lần !!!

Một biến thể khác về điều này sẽ là điêu khắc truy vấn trực tiếp:

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;

LƯU Ý: Nếu bảng không tồn tại, truy vấn được thực hiện là SELECT 1.

Đây là một mẫu cho thấy nếu bảng không tồn tại:

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>

Bây giờ, đây là một mẫu nơi bảng tồn tại:

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>

Tôi sẽ đề nghị thêm một phiên bản trong đó phiên bản này hoạt động, vì chức năng CONCAT chỉ khả dụng 2012 + ...
Fireshark519

@ Fireshark519 MySQL 5.0 có CONCAT()chức năng. Bạn có thể gặp phải vấn đề tương tự mà ai đó đã gặp phải vào tháng 11 năm 2010 trong StackOverflow ( stackoverflow.com/questions/4148149/ ám ). Một trang web khác tuyên bố CONCAT()quay lại MySQL 3.23 ( techonthenet.com/mysql/fifts/concat.php )
RolandoMyQueryDBA

4

Theo tài liệu, tùy chọn IGNORE sẽ coi lỗi là cảnh báo.

DELETE IGNORE ...

Từ khóa IGNORE khiến MySQL bỏ qua tất cả các lỗi trong quá trình xóa hàng. (Các lỗi gặp phải trong giai đoạn phân tích cú pháp được xử lý theo cách thông thường.) Các lỗi bị bỏ qua do sử dụng IGNORE được trả lại dưới dạng cảnh báo.

Tôi không chắc liệu MySQL có coi bảng không tồn tại là "lỗi trong giai đoạn phân tích cú pháp" hay không. Nếu có, bạn có thể truy vấn information_schema cho tên bảng. Một cái gì đó dọc theo những dòng này nên hoạt động.

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

Các bảng tạm thời không xuất hiện trong chế độ xem information_schema. Nếu bảng của bạn là một bảng tạm thời, thì bạn có một lỗi trong mã của bạn. Bạn đang đánh rơi bàn trước khi bạn hoàn thành nó.


Cảm ơn rất nhiều vì đã phản hồi nhanh chóng. Tôi đã thử sử dụng IGNORE nhưng không có kết quả, tôi đoán bảng không tồn tại được tìm thấy trong giai đoạn phân tích cú pháp. Làm cách nào để xây dựng CHỌN và XÓA như một truy vấn? Từ những gì tôi hiểu, tôi không thể sử dụng các câu lệnh IF vì MySQL chỉ hỗ trợ chúng như các thủ tục được lưu trữ.
GTE

2
Bạn không thể làm điều đó trong một tuyên bố duy nhất. Tại sao bạn không muốn xử lý lỗi trong Ruby? (Không có nghĩa gì khi không xử lý lỗi trong Ruby.)
Mike Sherrill 'Cat Recall'

Mẹo về tên cơ sở dữ liệu, bạn chỉ có thể sử dụng DATABASE () để sử dụng lược đồ hiện được chọn.
Garr Godfrey

DELETE IGNOREkhông im lặng lỗi trên một bảng không tồn tại. #1146 - Table 'nonexistent_tablename' doesn't exist
mickmackusa

3

Tôi sẽ kiểm tra bảng tồn tại trước, bằng cách chạy một truy vấn như;

SHOW TABLES LIKE 'table1';

Và sau đó thực hiện xóa dựa trên kết quả của truy vấn đó. Nếu bảng tồn tại, một bản ghi được trả về có chứa tên bảng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.