主外鍵表關聯數據的同時刪除

       今天產品有個操作,要求將滿足某個條件的主表和相關聯的幾個子表的數據全部刪除,其實這個要求很簡單,如果子表在創建外鍵的時候指定了ON DELETE CASCADE,則直接從主表中刪除相關記錄,子表中數據也會一起刪除。但是現在的子表外鍵創建時候沒有加此語句,看來此方法不通;
        接着想到了mysql裏面支持一次進行多表刪除,即delete a,b from a,b where ...這種語法,嘗試如下:

mysql> delete a,b from parent a,child2 b where a.id=b.parent_id and a.id=2;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test/child2`, CONSTRAINT `child2_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`))

 顯然,違反了外鍵約束而報錯,也就是說上述語句是按照先主後從的的順序進行刪除操作,而從表中由於存在相應記錄而導致刪除失敗;那麼,如果將表的刪除順序顛倒一下就可以按照先從後主的順序刪除呢?我們測試一下:

mysql> delete a,b from child2 a,parent b where a.parent_id=b.id and b.id=2;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test/child2`, CONSTRAINT `child2_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`))

看來還是不行,母子表的關聯刪除看來和表的連接順序無關,我們再次驗證一下:
mysql> set profiling=1;
Query OK, 0 rows affected (0.03 sec)

mysql> select @@profiling;
+-------------+
| @@profiling |
+-------------+
|           1 |
+-------------+
1 row in set (0.00 sec)

mysql> show profiles;
+----------+------------+--------------------+
| Query_ID | Duration   | Query              |
+----------+------------+--------------------+
|        1 | 0.00011900 | select @@profiling |
+----------+------------+--------------------+
1 row in set (0.00 sec)

mysql> delete a,b from child2 a,parent b where a.parent_id=b.id and b.id=2;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test/child2`, CONSTRAINT `child2_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`))
mysql> show profiles;
+----------+------------+---------------------------------------------------------------------+
| Query_ID | Duration   | Query                                                               |
+----------+------------+---------------------------------------------------------------------+
|        1 | 0.00011900 | select @@profiling                                                  |
|        2 | 0.03273800 | delete a,b from child2 a,parent b where a.parent_id=b.id and b.id=2 |
+----------+------------+---------------------------------------------------------------------+
2 rows in set (0.00 sec)

mysql> show profile for query 2;
+--------------------------+----------+
| Status                   | Duration |
+--------------------------+----------+
| (initialization)         | 0.000077 |
| init                     | 0.000011 |
| Opening tables           | 0.000018 |
| System lock              | 0.000009 |
| Table lock               | 0.00002  |
| init                     | 0.000027 |
| deleting from main table | 0.000007 |
| optimizing               | 0.000016 |
| statistics               | 0.000106 |
| preparing                | 0.000024 |
| executing                | 0.000007 |
| Sending data             | 0.000615 |
| end                      | 0.000017 |
| query end                | 0.000008 |
| freeing items            | 0.000015 |
| closing tables           | 0.031749 |
| logging slow query       | 0.000012 |
+--------------------------+----------+
17 rows in set (0.03 sec)

可以看到,執行步驟中有一句“deleting from main table”,也就是從主表刪除,而沒有刪除子表的語句,看來子表的數據按照這種方式是無法刪除的。

        那麼如果指定了on delete cascade,執行步驟又是什麼樣呢?
mysql> delete parent from parent where id=2;;
Query OK, 1 row affected (0.03 sec)
mysql> show profile for query 14
    -> ;
+--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| (initialization)               | 0.000062 |
| init                           | 0.000008 |
| Opening tables                 | 0.000018 |
| System lock                    | 0.000008 |
| Table lock                     | 0.000018 |
| init                           | 0.000023 |
| deleting from main table       | 0.000007 |
| optimizing                     | 0.000012 |
| statistics                     | 0.000068 |
| preparing                      | 0.000015 |
| executing                      | 0.000007 |
| Sending data                   | 0.000145 |
| deleting from reference tables | 0.000007 |
| end                            | 0.02809  |
| query end                      | 0.000014 |
| freeing items                  | 0.000016 |
| closing tables                 | 0.000017 |
| logging slow query             | 0.000006 |
+--------------------------------+----------+
18 rows in set (0.00 sec)

這次,多了一步“deleting from reference tables ”,顯然主從表都會進行刪除操作的。

最後,只能按照最笨的辦法,先刪除子表,再刪除主表:
mysql> delete from child2 where id=3;
Query OK, 0 rows affected (0.00 sec)

mysql> delete from parent where id=3;
Query OK, 1 row affected (0.03 sec)

如果有多層的母子表,則按照從最內層的子表開始,一直到最外層的主表的順利來完成刪除操作,如下:
delete d
from a,b,c,d
where a.id=b.order_id  and b.id=c.order_record_id  and c.id=d.ORDER_FACTORY_ID
and a.old_id>=20171059 and (a.card_type_id=21 or a.card_type_id=22) and a.guangzhou_order_id is not null;

delete c
from a,b,c
where a.id=b.order_id  and b.id=c.order_record_id
and a.old_id>=20171059 and (a.card_type_id=21 or a.card_type_id=22) and a.guangzhou_order_id is not null;

delete b
from a,b
where a.id=b.order_id
and a.old_id>=20171059 and (a.card_type_id=21 or a.card_type_id=22) and a.guangzhou_order_id is not null;

delete a
from order_tab a
where a.old_id>=20171059 and (a.card_type_id=21 or a.card_type_id=22) and a.guangzhou_order_id is not null;

看來設置外鍵的時候還是應該考慮好on delete 和on udpate的操作action,防止這些無謂的麻煩。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章