当存在子查询时,为什么在外部查询中使用 DISTINCT 会使查询时间增加 30000 倍?

问题描述 投票:0回答:1

(使用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 sqlite database-performance
1个回答
0
投票

理想情况下不建议在 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;

如果您需要进一步帮助,请告诉我

© www.soinside.com 2019 - 2024. All rights reserved.