SQLite 非常慢的 SELECT 时间
Posted
技术标签:
【中文标题】SQLite 非常慢的 SELECT 时间【英文标题】:SQLite very slow SELECT time 【发布时间】:2013-01-30 08:18:20 【问题描述】:我的程序出现了一些奇怪的行为,也许你可以给它带来一些启示。
今天我开始测试一些代码,并意识到一个特定的查询真的很慢(大约需要 2 分钟)。
这里选择:
select distinct table1.someName
from table1
INNER JOIN table2 ON table2.id = table1.t2_id
INNER JOIN table3 ON table1.id = table3.t1_id
INNER JOIN table4 ON Table3.id = table4.t3_id
INNER JOIN table5 ON table5.id = table4.t5_id
INNER JOIN table6 ON table4.id = table6.t4_id
where t4_name = 'whatever'
and t2_name = 'moarWhatever'
and timestamp_till is null
order by someName
问题是,结果是大约 120 条记录。 INNER JOIN
s 将 timestamp_till is null
的检查数量减少到每条记录大约 20 条记录。
最让我烦恼的是,我已经测试将整个表 table6
插入到一个新创建的表中,并将 timestamp_till
重命名为 ende
。在该表上,选择大约在 0.1 秒内完成 ...
timestamp_till 是 SQLite3 的某种保留名称吗?这可能是 SQLite 引擎中的错误吗?是我的错吗? oO
编辑:添加EXPLAIN QUERY PLAN
输出...
当使用and timestamp_till is null
查询时,他给出:
0|0|4|SEARCH TABLE table5 USING COVERING INDEX sqlite_autoindex_table5 (t4_name=?) (~1 rows)
0|1|3|SEARCH TABLE table4 USING INDEX table4.fk_table4_1_idx (t5_id=?) (~10 rows)
0|2|2|SEARCH TABLE table3 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0|3|0|SEARCH TABLE table1 USING INTEGER PRIMARY KEY (rowid=?) (~1rows)
0|4|1|SEARCH TABLE table2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0|5|5|SEARCH TABLE table6 USING INDEX table6.fk_table6_ts_till (timestamp_till=?) (~2 rows)
0|0|0|USE TEMP B-TREE FOR GROUP BY
0|0|0|USE TEMP B-TREE FOR DISTINCT
最快的是:
select distinct table1.someName
from table1
INNER JOIN table2 ON table2.id = table1.t2_id
INNER JOIN table3 ON table1.id = table3.t1_id
INNER JOIN table4 ON Table3.id = table4.t3_id
INNER JOIN table5 ON table5.id = table4.t5_id
INNER JOIN table6 ON table4.id = table6.t4_id
where t4_name = 'whatever'
and t2_name = 'moarWhatever'
order by someName
及其结果:
0|0|4|SEARCH TABLE table5 USING COVERING INDEX sqlite_autoindex_table5_1 (t4name=?) (~1 rows)
0|1|3|SEARCH TABLE table4 USING INDEX table4.fk_table4_1_idx (t5_id=?) (~10 rows)
0|2|2|SEARCH TABLE table3 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0|3|0|SEARCH TABLE table1 USING INTEGER PRIMARY KEY (rowid=?) (~1rows)
0|4|1|SEARCH TABLE table2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0|5|5|SEARCH TABLE table6 USING COVERING INDEX sqlite_autoindex_table6_1 (id=?) (~10 rows)
0|0|0|USE TEMP B-TREE FOR GROUP BY
0|0|0|USE TEMP B-TREE FOR DISTINCT
测试表是table6
的副本
0|0|4|SEARCH TABLE table5 USING COVERING INDEX sqlite_autoindex_table5_1 (name=?) (~1 rows)
0|1|3|SEARCH TABLE table4 USING INDEX table4.fk_t5_idx (t5_id=?) (~10 rows)
0|2|2|SEARCH TABLE table3 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0|3|0|SEARCH TABLE table1 USING INTEGER PRIMARY KEY (rowid=?) (~1rows)
0|4|1|SEARCH TABLE table2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0|5|5|SEARCH TABLE test USING INDEX test.fk_test__idx (id=?) (~2 rows)
0|0|0|USE TEMP B-TREE FOR GROUP BY
0|0|0|USE TEMP B-TREE FOR DISTINCT
为test
创建脚本
CREATE TABLE "test"(
"id" INTEGER NOT NULL,
"t12_id" INTEGER NOT NULL,
"value" DECIMAL NOT NULL,
"anfang" INTEGER NOT NULL,
"ende" INTEGER DEFAULT NULL,
PRIMARY KEY("id","t12_id","anfang"),
CONSTRAINT "fk_test_t12_id"
FOREIGN KEY("t12_id")
REFERENCES "table12"("id"),
CONSTRAINT "fk_test_id"
FOREIGN KEY("id")
REFERENCES "id_col"("id"),
CONSTRAINT "fk_test_anfang"
FOREIGN KEY("anfang")
REFERENCES "ts_col"("id"),
CONSTRAINT "fk_test_ende"
FOREIGN KEY("ende")
REFERENCES "ts_col"("id")
);
CREATE INDEX "test.fk_test_idx_t12_id" ON "test"("t12_id");
CREATE INDEX "test.fk_test_idx_id" ON "test"("id");
CREATE INDEX "test.fk_test_anfang" ON "test"("anfang");
CREATE INDEX "test.fk_test_ende" ON "test"("ende");
苏龙仔
【问题讨论】:
EXPLAIN QUERY PLAN 对于这两个查询的输出是什么? 加入 2 个表需要一些时间,具体取决于它们都有多少条目,而不是最终结果。加入 6 个表将需要更多时间。确保所有外键都被索引,这应该会有所帮助。 @CL。添加到问题@MPelletier 是的,我知道,但与timestamp_till is null
相同的查询需要1-2 分钟,甚至不需要一秒钟......
您能否详细说明timestamp_till
和someName
在哪个表中?
添加了快速查询以及timesamp_till
的来源。
【参考方案1】:
第一个注意事项:SQLite 将在其查询中仅使用 1 个索引。再也没有(使用当前版本)。
因此,这就是 SQLite 正在做的事情:
慢查询:使用timestamp_till
上的索引
快速查询(无 timestamp_till):使用 table6.id
上的(自动)索引。
我看到了两种解决方法。
您可以使用子查询:
select distinct SomeName FROM
(
select table1.someName as "SomeName", timestamp_till
from table1
INNER JOIN table2 ON table2.id = table1.t2_id
INNER JOIN table3 ON table1.id = table3.t1_id
INNER JOIN table4 ON Table3.id = table4.t3_id
INNER JOIN table5 ON table5.id = table4.t5_id
INNER JOIN table6 ON table4.id = table6.t4_id
where t4_name = 'whatever'
and t2_name = 'moarWhatever'
) Q
where timestamp_till is null
order by SomeName;
如果您在其他地方不需要,也可以将索引放在timestamp_till
。
可能还可以通过重新排序连接来提高速度。通常最小的表首先会更快,但这可能会有很大差异。
【讨论】:
很酷,看起来很有希望,但它如何解释,以timestamp_till
为其他名称的 1:1 副本上的“慢查询”可以超级快,而在原始表上不行?
@Zaiborg 你的其他名字也被索引了吗?
是的,我从重命名的创建脚本创建了整个测试
@Zaiborg 我开始怀疑“1 索引”规则。我没有看到您的查询,timestamp_till
有另一个名称。
查询是一样的,我只是复制了表并将timestamp_till
重命名为ende
以确保它不能是名称冲突......今天“慢查询”是第一个尝试像“快速”(即时结果),然后像往常一样缓慢运行......以上是关于SQLite 非常慢的 SELECT 时间的主要内容,如果未能解决你的问题,请参考以下文章