【问题标题】:Automatically remove a row without foreign references自动删除没有外部引用的行
【发布时间】:2020-09-17 18:03:21
【问题描述】:

我正在使用 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)
    );

我想确保“货币”表中未被“table1”和“table2”中的任何行引用的行将被自动删除。这应该表现得像某种引用计数的对象。当引用计数达到零时,应删除“货币”表中的相关行。

解决这个问题的“SQL方式”是什么?

如果可以带来优雅的解决方案,我愿意重新设计我的表格。 我更喜欢避免需要应用程序方面额外工作的解决方案,或者需要定期清理的解决方案。

【问题讨论】:

    标签: sql database sqlite database-design


    【解决方案1】:

    table1table2 中分别创建一个AFTER DELETE TRIGGER

    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;
    

    每次删除table1table2中的一行时,所涉及的触发器将检查另一个表是否包含已删除的currency,如果不包含它,则会从@中删除987654329@.

    请参阅demo

    【讨论】:

      【解决方案2】:

      没有自动执行此操作的方法。反过来可以使用级联删除外键引用来处理。反之亦然,当一种货币被删除时,所有相关的行都会被删除。

      您可以每天安排一个作业,例如:

      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);
            
      

      【讨论】:

        【解决方案3】:

        如果您需要自动执行此操作,那么大多数 dbms 都提供了触发机制。您可以在运行以下查询的更新和删除操作上创建触发器:

        您可以为此使用左连接:

        https://www.w3schools.com/sql/sql_join_left.asp

        它为左表中的所有行返回一行,即使右表中没有对应的行,也将右表中的行替换为null。然后,您可以检查一个不为空的右表字段是否为空且为空。这将过滤右表中没有对应的行。

        例如:

        SELECT currencies.currency FROM currencies LEFT JOIN table1 WHERE table1.currency IS NULL
        

        将显示 table1 的相关行。 您可以对表 2 执行相同操作。

        这将为您提供两个查询,显示哪些行没有对应部分。 然后,您可以在结果上使用 intersect ,这样您就可以得到其中任何一个都没有 couterpart 的行:

        SELECT * FROM query1 INTERSECT SELECT * FROM query2
        

        现在您有了要删除的货币列表。

        您可以通过使用子查询删除来完成此操作:

        DELETE FROM currencies WHERE currency IN (SELECT ...)
        

        【讨论】:

        • 另一个答案中的查询也可以。
        • 这种带触发器的操作可能会花费更多。
        猜你喜欢
        • 1970-01-01
        • 2014-02-13
        • 1970-01-01
        • 2010-11-20
        • 2018-05-15
        • 1970-01-01
        • 1970-01-01
        • 2012-07-09
        • 2012-03-26
        相关资源
        最近更新 更多