问题描述
我正在使用sqlite3。 我有一个“货币”表,还有两个表使用外键引用货币表,如下所示:
CREATE TABLE currencies (
currency TEXT NOT NULL PRIMARY KEY
);
CREATE TABLE table1 (
currency TEXT NOT NULL PRIMARY KEY,FOREIGN KEY(currency)
REFERENCES currencies(currency)
);
CREATE TABLE table2 (
currency TEXT NOT NULL PRIMARY KEY,FOREIGN KEY(currency)
REFERENCES currencies(currency)
);
我想确保“ currency”表中未被“ table1”和“ table2”的任何行引用的行将被自动删除。它的行为应类似于某种ref-counted对象。当参考计数达到零时,应删除“货币”表中的相关行。
什么是解决此问题的“ SQL方式”?
我愿意重新设计我的桌子,如果它可以导致一个优雅的解决方案。 我宁愿避免需要在应用程序方面进行额外工作的解决方案,或者需要定期清理的解决方案。
解决方法
没有自动执行此操作的方法。相反,可以使用级联删除外键引用进行处理。相反的是,删除货币后,所有相关行都被删除。
您可以安排每天执行的工作,例如:
delete from currencies c
where not exists (select 1 from table1 t1 where t1.currency = c.currency) and
not exists (select 1 from table2 t2 where t2.currency = c.currency);
,
如果您需要自动执行此操作的方法,那么大多数dbms都会提供触发机制。您可以在运行以下查询的更新和删除操作上创建触发器:
您可以为此使用左联接:
https://www.w3schools.com/sql/sql_join_left.asp
即使左侧表中没有对应的行,它也会为左侧表中的所有行返回一行,将右侧的行替换为null。然后,您可以检查一个不为null的右表字段,其中null为null。这将过滤右表中没有对应行的行。
例如:
SELECT currencies.currency FROM currencies LEFT JOIN table1 WHERE table1.currency IS NULL
将显示表1的相关行。 您可以对表二做同样的事情。
这将给您两个查询,显示哪些行没有couterpart。 然后,您可以在结果上使用相交,以使行中的任何一行都没有couterpart:
SELECT * FROM query1 INTERSECT SELECT * FROM query2
现在,您有了要删除的货币列表。
您可以通过使用子查询删除来完成此操作:
DELETE FROM currencies WHERE currency IN (SELECT ...)
,
分别在AFTER DELETE TRIGGER
和table1
中创建一个table2
:
CREATE TRIGGER remove_currencies_1 AFTER DELETE ON table1
BEGIN
DELETE FROM currencies
WHERE currency = OLD.currency
AND NOT EXISTS (SELECT 1 FROM table2 WHERE currency = OLD.currency);
END;
CREATE TRIGGER remove_currencies_2 AFTER DELETE ON table2
BEGIN
DELETE FROM currencies
WHERE currency = OLD.currency
AND NOT EXISTS (SELECT 1 FROM table1 WHERE currency = OLD.currency);
END;
每次您删除table1
或table2
中的行时,所涉及的触发器都会检查另一个表是否包含已删除的currency
,如果不包含该行,将从currencies
中删除。
请参见demo。