Jes*_*ams 3 sql-server query-performance
我有一个针对可以增长到数百万行的表执行的查询。查询来自我们使用的 QA 工具,它超出了数据库的标准功能(就索引的内容以及索引方式和原因而言)。查询是:
SELECT id FROM thisTable t
WHERE col = 'val'
AND ((not exists (SELECT 1 FROM thisTable WHERE refid = t.id) and refbool = 0) or refbool = 1)
ORDER BY newid()
Run Code Online (Sandbox Code Playgroud)
基本上,假设表中有id,refid,refbool,和col列。所以你可以有如下数据:
id | refid | refbool | col
------------------------------------
1 | NULL | 0 | val
2 | NULL | 0 | val
3 | NULL | 0 | val
4 | 2 | 1 | val
5 | NULL | 0 | val
6 | 1 | 1 | val
Run Code Online (Sandbox Code Playgroud)
查询永远不应该为 (1, 2) 中的 id 选择行,因为它们被其他行引用。它应该只抓取行,其中refbool = 1, OR refbool = 0AND 该行的 id 不是任何其他行的refid。这个语句非常糟糕,但我不确定更好的查询会是什么样的。假设不能添加索引、视图、存储过程或其他底层机制——它必须是一个查询。
整个查询要大得多,JOINS有两个额外的表,并收集了相当多的数据。但是,我已将它缩小到这一特定位,因为注释掉这一行会将查询执行时间从 16 秒缩短到 <1 秒。
我还对行重新排序,newid()因为我需要随机选择一个示例项目。ORDER BY即使保留第三行,删除也使查询速度显着加快。这两个操作的结合似乎导致了缓慢。我曾尝试设计 CTE,但未能提高性能。
我看过执行计划。将添加可以改进此查询的索引。但是,内部 QA 工具的性能并不优先于客户端生产环境中的性能,并且在 QA 环境中对与索引相关的实用程序的结构进行更改等。使其作为 QA 环境的有用性无效,因为它可能会执行与生产环境不同。
通过更改查询本身的逻辑,我当然可以编写一个比我当前的查询性能更差的查询。我相信我们都可以。我要求应用这种推理来提高查询的性能。
执行计划未包括在内,但此类查询的典型问题(排序除外)是优化器选择嵌套循环反半连接而没有良好的支持索引。它也可能是一个流氓 top (1),或者向带有嵌套启动过滤器和反半连接的半连接的性能不佳的转换。
无论如何,有两种常用的解决方法:
OR手动重写为 a UNION(或者,如果保证不相交,则重写为 a UNION ALL)。NOT EXISTS为左连接,过滤保留的一侧为NULL。以下内容包含两者:
DECLARE @thisTable table
(
id integer PRIMARY KEY,
refid integer NULL,
refbool bit NOT NULL,
col varchar(10) NOT NULL
);
INSERT @thisTable
(id, refid, refbool, col)
VALUES
(1, NULL, 0, 'val'),
(2, NULL, 0, 'val'),
(3, NULL, 0, 'val'),
(4, 2 , 1, 'val'),
(5, NULL, 0, 'val'),
(6, 1 , 1, 'val');
Run Code Online (Sandbox Code Playgroud)
SELECT
U.id
FROM
(
-- T.refbool = 1
SELECT T.id
FROM @thisTable AS T
WHERE
T.col = 'val'
AND T.refbool = 1
-- Or (disjoint)
UNION ALL
-- T.refbool = 0 and not exists
SELECT T.id
FROM @thisTable AS T
LEFT JOIN @thisTable AS T2
ON T2.refid = T.id
WHERE
T.col = 'val'
AND T.refbool = 0
AND T2.id IS NULL
) AS U
ORDER BY
CHECKSUM(NEWID());
Run Code Online (Sandbox Code Playgroud)
有关随机排序的更多选择,请参阅现有的问答:
不要只尝试最佳答案。