(使用SQLite v3.35.5)
考虑这个查询(不用担心具体细节——它只是一个带有两个子查询来获取计数的
DISTINCT
选择):
SELECT DISTINCT name,
(SELECT COUNT(name) FROM sections s2 WHERE s1.name = s2.name) AS section_count,
(SELECT COUNT(DISTINCT document_key) FROM sections s3 WHERE s1.name = s3.name) AS document_count
FROM sections s1
该查询在只有 15,000 行的表上运行始终需要超过 30 秒。
Result: 249 rows returned in 32574ms
这对我来说似乎非常非常慢。 (我在
name
列上确实有一个索引。)
现在——如果我从第一行删除
DISTINCT
,那么它就会打开:
SELECT name...
现在需要1ms到3ms。
Result: 15729 rows returned in 1ms
是的,删除该
DISTINCT
可以将查询速度提高约 30000 倍,尽管它会导致返回的行数增加约 60 倍。
仅当存在子查询时才会发生这种情况。这两个查询...
SELECT name FROM sections
SELECT DISTINCT name FROM sections
...两者都在 1-3 毫秒内执行。
我在删除任一子查询的情况下进行了测试。删除第一个并没有改变任何东西,但是删除第二个将查询时间减少到大约 3 秒。这意味着问题似乎在于
DISTINCT
性能,无论是在外部查询还是(第二个)内部查询中,尤其是在两者的组合中。
为什么会这样呢?丢失
DISTINCT
需要子查询执行更频繁,因此理论上应该会更慢。
理想情况下不建议在 SQL 中使用子查询,因为它已被证明不是最优的,并且会导致很多性能问题。 在您的情况下,最可能的原因是当 SQL 尝试获取
sections s1
的相应记录时,您的子查询会在每次迭代中执行
我认为你有两种选择来优化性能
使用预加载来获取数据(强烈推荐)
您可以通过 SQL 中的 JOINS 使用预加载来建立表之间的关系。这极大地简化了您的查询并使数据检索速度更快。所以在你的情况下
SELECT s1.name,
COUNT(s2.name) AS section_count,
COUNT(DISTINCT s3.document_key) AS document_count
FROM sections s1
JOIN sections s2 ON s1.name = s2.name
LEFT JOIN sections s3 ON s1.name = s3.name
GROUP BY s1.name;
使用CTE
在某些情况下,您可以使用公共表表达式(CTE)来提高性能,特别是在 SQLite 中,通过简化查询执行并避免冗余计算。 CTE 可以帮助优化此查询,因为它们允许重用计算值,而无需为每行重新计算它们。
在你的情况下,这就是它的样子
WITH section_counts AS (
SELECT name, COUNT(*) AS section_count
FROM sections
GROUP BY name
),
document_counts AS (
SELECT name, COUNT(DISTINCT document_key) AS document_count
FROM sections
GROUP BY name
)
SELECT sc.name, sc.section_count, dc.document_count
FROM section_counts sc
JOIN document_counts dc ON sc.name = dc.name;
如果您需要进一步帮助,请告诉我