【问题标题】:Why does incorrect type affinity on a foreign key column makes joins very slow in SQLite?为什么外键列上不正确的类型关联会导致 SQLite 中的连接非常慢?
【发布时间】:2014-03-30 09:23:33
【问题描述】:

在我们的应用程序中,我们使用字母数字序列号跟踪制造的组件,这为我们提供了一个自然密钥。如您所料,我们选择的类型亲和力是TEXT。这些组件是应用程序的核心,因此有许多表通过此序列号上的外键关系链接。

在一个特定表上,序列号FOREIGN KEY 列的类型关联被错误地声明为INTEGER。由于类型关联系统的灵活性,这从未成为问题,因为我们能够毫无问题地将 INSERT TEXT 值放入列中。一切似乎都运行良好。

随着数据库适度增长,一些查询变得非常缓慢,在现代 PC 上大约为几秒钟。在确保由于FOREIGN KEYPRIMARY KEY 声明所有相关列已经被自动索引后,我找到了罪魁祸首。将坏表上的类型亲和性从INTEGER 更改为TEXT* 后,加入该表的慢查询速度加快了两个数量级。

我对最终结果很满意,但我很想知道为什么会这样?由于声明的类型亲和性,SQLite 是否在底层构建了不同类型的索引?比较中类型亲和性的差异是否会阻止查询使用索引?是否有一些不必要的类型转换导致速度变慢?

这是一个例子:

CREATE TABLE IF NOT EXISTS pcb_units (
    serial_no TEXT NOT NULL,    
    PRIMARY KEY (serial_no)
);

CREATE TABLE assembly_pcb_units (
    pcb_serial_no INTEGER NOT NULL,    -- This is the bad column
    assembly_serial_no TEXT NOT NULL,
    added_time TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
    added_by TEXT NOT NULL,
    CONSTRAINT pcb_in_one_assembly_only
        UNIQUE(pcb_serial_no),
    FOREIGN KEY (assembly_serial_no)
        REFERENCES assembly_units(assembly_serial_no),
    FOREIGN KEY (pcb_serial_no)
        REFERENCES pcb_units(serial_no),
    FOREIGN KEY (added_by)
        REFERENCES sti_users(user_id)
);

* 我真的希望 SQLite 支持 ALTER TABLE。事实上,我不得不禁用外键约束,删除表,重新创建它,然后重新插入所有行。

【问题讨论】:

  • 请显示一个示例数据库架构。
  • 添加了示例架构。
  • 外键约束不添加任何索引。请显示新旧表查询的 EXPLAIN QUERY PLAN 输出。
  • 哇,你说得对。我不知道我从哪里得到他们这样做的想法。即使没有外键上的索引,我的查询目前也在相当合理的时间内运行。我想我会在子键上添加一些索引,看看会有什么不同..
  • 其实我改变了主意。文档 (sqlite.org/foreignkeys.html#fk_indexes) 说没有自动索引,但似乎实际上有: SELECT * FROM sqlite_master WHERE name LIKE '%autoindex%';

标签: sql sqlite join types foreign-keys


【解决方案1】:

从下面的演示中可以看出,列类型不匹配会阻止使用两个索引(请参阅 SCAN TABLE 父级),但奇怪的是,仅当 WHERE 子句位于 child_int 端时。这解释了您的查询速度较慢的原因。

C:\Users\DDevienne>sqlite3
SQLite version 3.8.3.1 2014-02-11 14:52:19
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> create table parent (sn text primary key, data text);
sqlite> create table child_int (sn integer references parent(sn), data text, unique(sn));
sqlite> create table child_txt (sn text    references parent(sn), data text, unique(sn));
sqlite> insert into parent values ('x', '1'), ('y', '2'), ('z', '3');
sqlite> insert into child_int values ('x', 'one'), ('y', 'two'), ('z', 'three');
sqlite> insert into child_txt values ('x', 'one'), ('y', 'two'), ('z', 'three');
sqlite> select p.data, c.data from parent p, child_int c on p.sn = c.sn where p.sn = 'y';
2|two
sqlite> select p.data, c.data from parent p, child_int c on p.sn = c.sn where c.sn = 'y';
2|two
sqlite> select p.data, c.data from parent p, child_txt c on p.sn = c.sn where p.sn = 'y';
2|two
sqlite> select p.data, c.data from parent p, child_txt c on p.sn = c.sn where c.sn = 'y';
2|two
sqlite> explain query plan select p.data, c.data from parent p, child_int c on p.sn = c.sn where p.sn = 'y';
0|0|0|SEARCH TABLE parent AS p USING INDEX sqlite_autoindex_parent_1 (sn=?)
0|1|1|SEARCH TABLE child_int AS c USING INDEX sqlite_autoindex_child_int_1 (sn=?)
sqlite> explain query plan select p.data, c.data from parent p, child_int c on p.sn = c.sn where c.sn = 'y';
0|0|1|SEARCH TABLE child_int AS c USING INDEX sqlite_autoindex_child_int_1 (sn=?)
0|1|0|SCAN TABLE parent AS p
sqlite> explain query plan select p.data, c.data from parent p, child_txt c on p.sn = c.sn where p.sn = 'y';
0|0|1|SEARCH TABLE child_txt AS c USING INDEX sqlite_autoindex_child_txt_1 (sn=?)
0|1|0|SEARCH TABLE parent AS p USING INDEX sqlite_autoindex_parent_1 (sn=?)
sqlite> explain query plan select p.data, c.data from parent p, child_txt c on p.sn = c.sn where c.sn = 'y';
0|0|0|SEARCH TABLE parent AS p USING INDEX sqlite_autoindex_parent_1 (sn=?)
0|1|1|SEARCH TABLE child_txt AS c USING INDEX sqlite_autoindex_child_txt_1 (sn=?)
sqlite>

【讨论】:

  • 也感谢您在 sqlite 邮件列表上的回复 :) 我很想看看那里是否有人可以解释不对称!
猜你喜欢
  • 2013-06-04
  • 1970-01-01
  • 2017-06-19
  • 2015-01-21
  • 2022-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多