SQLite):删除行后自动更新两个表中的索引

Posted

技术标签:

【中文标题】SQLite):删除行后自动更新两个表中的索引【英文标题】:SQL(ite): auto updating indexes in two tables after row deletion 【发布时间】:2013-10-16 16:08:30 【问题描述】:

再次在这里向你们寻求帮助!

我有两个问题要问你和两个 sql 表:

TABLE_1

[ ID_T1, T1_NAME,      T1_VALUE   ]

    0 |   name_t1_0 |  value_t1_0
    1 |   name_t1_1 |  value_t1_1
    2 |   name_t1_2 |  value_t1_2
    3 |   name_t1_3 |  value_t1_3

TABLE_2

[ ID_T2,  ID_T1_IN_T2,   T2_NAME,   T2_VALUE    ]

    0        | 0 |    name_t2_00 | value_t2_00
    1        | 0 |    name_t2_10 | value_t2_10
    2        | 0 |    name_t2_20 | value_t2_20
    3        | 0 |    name_t2_30 | value_t2_30
    0        | 1 |    name_t2_01 | value_t2_01
    1        | 1 |    name_t2_11 | value_t2_11
    0        | 2 |    name_t2_02 | value_t2_02
    1        | 2 |    name_t2_12 | value_t2_12

ID_T1 是唯一的渐进式索引。 TABLE_1 中的每个 ID_T1 可以在 ID_T1_IN_T2 列中出现 N 次。

ID_T2 是与每个 ID_T1 相关联的渐进式索引。

这意味着对于 TABLE_1 中的每一行,我们可以在 TABLE_2 中有 N 行。

问题 #1:如何在从 TABLE_1 中删除一行时自动更新 ID_T1_IN_T2 值?

示例:从表 1 中删除 ID_T1 = 0 的行。预期结果如下(0 后的 ID_T1 减少,ID_T1_IN_T2 也减少)

TABLE_1

 [ ID_T1,  T1_NAME,    T1_VALUE   ]
    0 |   name_t1_1 |  value_t1_1
    1 |   name_t1_2 |  value_t1_2
    2 |   name_t1_3 |  value_t1_3

表_2

[ ID_T2,  ID_T1_IN_T2,   T2_NAME,     T2_VALUE    ]
    0        | 0 |      name_t2_01 |  value_t2_01
    1        | 0 |      name_t2_11 |  value_t2_11
    0        | 1 |      name_t2_02 |  value_t2_02
    1        | 1 |      name_t2_12 |  value_t2_12

问题 #2:如何在删除行时自动更新 ID_T2 值?

示例:从刚刚更新的 TABLE_2 中删除 ID_T2 = 0 且 ID_T1_IN_T2=0 的行。预期结果如下(0后的ID_T2减少,ID_T1_IN_T2保持不变)

[ ID_T2,  ID_T1_IN_T2,   T2_NAME,     T2_VALUE    ]
    0        | 0 |      name_t2_11 |  value_t2_11
    0        | 1 |      name_t2_02 |  value_t2_02
    1        | 1 |      name_t2_12 |  value_t2_12

在这些情况下你建议怎么做?

请注意,删除表并重新创建它们不被视为一种解决方案,因为它很脏而且性能极差(这是我暂时采用的解决方案)。

【问题讨论】:

您是否在存储派生值? 数据库刚刚被 C++ 应用程序填满,查询总是类似于“select t*name from table* where id* = x and id_* = y ”。当 c++ 端的名称或值发生更改时,它会在 db 中正确更新。如果在 c++ 端删除了一个项目,这就是问题所在:) 【参考方案1】:

您的 C++ 应用程序没有使用好的数据库方法。我建议您使用 views 和 triggers 解决此问题。

例子:

您的主表变为T1T2

CREATE TABLE T1(ID, T1_NAME, T1_VALUE);
CREATE TABLE T2(ID, ID_T1, T2_NAME, T2_VALUE);

那些ID 已修复!你不应该改变它们!

接下来,辅助视图,将真实的ID 映射到动态视图:

CREATE VIEW T1_IDX AS SELECT DISTINCT ID, (SELECT COUNT() FROM (SELECT DISTINCT ID FROM T1 AS _ WHERE ID<T1.ID)) AS ID_T1 FROM T1;
CREATE VIEW T2_IDX AS SELECT DISTINCT ID, (SELECT COUNT() FROM (SELECT DISTINCT ID FROM T2 AS _ WHERE ID<T2.ID)) AS ID_T2 FROM T2;

TABLE_1TABLE_2 被模拟为视图:

CREATE VIEW TABLE_1 AS
    SELECT ID_T1, T1_NAME, T1_VALUE FROM T1
    JOIN T1_IDX ON T1.ID=T1_IDX.ID;
CREATE VIEW TABLE_2 AS
    SELECT ID_T2, T1_IDX.ID_T1 AS ID_T1_IN_T2, T2_NAME, T2_VALUE FROM T2
    JOIN T2_IDX ON T2.ID=T2_IDX.ID
    JOIN T1_IDX ON T2.ID_T1=T1_IDX.ID;

并且TABLE_1 上的DELETE 语句与以下TRIGGER 映射:

CREATE TRIGGER TABLE_1_DELETE INSTEAD OF DELETE ON TABLE_1 BEGIN
    DELETE FROM T2 WHERE ID_T1 = (SELECT ID FROM T1_IDX WHERE ID_T1=OLD.ID_T1);
    DELETE FROM T1 WHERE ID = (SELECT ID FROM T1_IDX WHERE ID_T1=OLD.ID_T1);
END;

通过这种自动化,您的 C++ 应用程序将看到带有计算索引的TABLE_1,而不是真实索引,并且DELETE 操作由 SQLite 引擎处理以对真实表进行操作。

注意:除SELECTDELETE FROM TABLE_1 之外的视图上的其他操作,如果需要,必须作为触发器实现!

【讨论】:

非常有趣的方法,我会尽快尝试:) 谢谢【参考方案2】:

另一个更简单的选择。

只需添加一个触发器:

CREATE TRIGGER TABLE_1_DELETE AFTER DELETE ON TABLE_1 BEGIN
    UPDATE TABLE_1 SET ID_T1=ID_T1-1 WHERE ID_T1>OLD.ID_T1;
    -- DELETE FROM TABLE_2 WHERE ID_T1_IN_T2=OLD.ID_T1;
    UPDATE TABLE_2 SET ID_T1_IN_T2=ID_T1_IN_T2-1 WHERE ID_T1_IN_T2>OLD.ID_T1;
    END;
CREATE TRIGGER TABLE_2_DELETE AFTER DELETE ON TABLE_2 BEGIN
    UPDATE TABLE_2 SET ID_T2=ID_T2-1 WHERE ID_T2>OLD.ID_T2 AND ID_T1_IN_T2=OLD.ID_T1_IN_T2;
    END;

这些触发器将在删除后立即更新索引。

如果删除注释行,删除TABLE_1 也会导致删除TABLE_2 中的对应记录。

【讨论】:

以上是关于SQLite):删除行后自动更新两个表中的索引的主要内容,如果未能解决你的问题,请参考以下文章

CoreData 不会删除自动生成的关系表中的记录

更新/插入/删除表中的行后触发的触发器有问题

CakePHP:删除行后如何使自动增量值再次连续

oracle索引

android怎么实现sqlite的增删改查

SQLite 中的主键是不是需要索引?