使用 TOP 和 ORDER BY 查询非常慢
Very slow query with TOP and ORDER BY
我在 SQL Server 2014 中有一个查询,当我执行它时需要很长时间才能获得结果。
当我删除TOP
或ORDER BY
指令时,它执行得更快,但如果我把它们都写下来,就会花费很多时间。
SELECT TOP (10) A.ColumnValue AS ValueA
FROM TableA AS A
INNER JOIN TableB AS B
ON A.ID = B.ID
WHERE A.DateValue > '1982-05-02'
ORDER BY ValueA
我怎样才能让它更快?
根据两个表的 ID
列创建索引
CREATE INDEX index_nameA
ON TableA (ID, DateValue)
;
CREATE INDEX index_nameB
ON TableB (ID)
它将在查询执行时创建更好的计划
最好的方法是使用索引来提高性能。
这里,在这种情况下,索引可以放在(date_value)上。
索引的使用参考这个URL:using indexes
除非你的大部分数据都有更早的日期,否则这是非常无望的。如果日期很特殊,您可以创建一个计算的持久列来总体上加快查询速度。但是,我怀疑情况是否如此。
我可以为这样表述的查询设想一个更好的执行计划:
SELECT TOP (10) A.ColumnValue AS ValueA
FROM TableA A
WHERE EXISTS (SELECT 1 FROM TableB b WHERE A.ID = B.ID) AND
A.DateValue > '1982-05-02'
ORDER BY ValueA;
在 TableA(ValueA, DateValue, Id, ColumnValue)
和 TableB(id)
上有一个索引。该执行计划将从头开始扫描索引,然后在 DateValue
和 Id
和 return ColumnValue
上针对相应的匹配行进行测试。
不过,我认为SQL服务器不会生成这个计划(尽管值得一试),如果不生成我不知道如何强制它。
你说
When I remove the TOP or the ORDER BY ... it executes faster
这表明 SQL 服务器可以按所需顺序生成整个结果集。它只是在 TOP 10
的限制下变成梨形。这是 rowgoals 的常见问题。当 SQL 服务器知道您只需要前几个结果时,它可以选择一个不同的计划来尝试针对这种可能适得其反的情况进行优化。
较新的版本包含提示 DISABLE_OPTIMIZER_ROWGOAL
以在每个查询的基础上禁用此功能。在旧版本上,您可以使用 QUERYTRACEON 4138
,如下所示。
SELECT TOP (10) A.ColumnValue AS ValueA
FROM TableA AS A
INNER JOIN TableB AS B
ON A.ID = B.ID
WHERE A.DateValue > '1982-05-02'
ORDER BY ValueA
OPTION (QUERYTRACEON 4138)
您可以使用它来验证原因,但可能会发现 运行 QUERYTRACEON
的权限有问题。
在这种情况下,您可以隐藏变量中的 TOP
值,如下所示
DECLARE @Top INT = 10
SELECT TOP (@Top) A.ColumnValue AS ValueA
FROM TableA AS A
INNER JOIN TableB AS B
ON A.ID = B.ID
WHERE A.DateValue > '1982-05-02'
ORDER BY ValueA
option (optimize for (@Top = 1000000))
我在 SQL Server 2014 中有一个查询,当我执行它时需要很长时间才能获得结果。
当我删除TOP
或ORDER BY
指令时,它执行得更快,但如果我把它们都写下来,就会花费很多时间。
SELECT TOP (10) A.ColumnValue AS ValueA
FROM TableA AS A
INNER JOIN TableB AS B
ON A.ID = B.ID
WHERE A.DateValue > '1982-05-02'
ORDER BY ValueA
我怎样才能让它更快?
根据两个表的 ID
列创建索引
CREATE INDEX index_nameA
ON TableA (ID, DateValue)
;
CREATE INDEX index_nameB
ON TableB (ID)
它将在查询执行时创建更好的计划
最好的方法是使用索引来提高性能。 这里,在这种情况下,索引可以放在(date_value)上。 索引的使用参考这个URL:using indexes
除非你的大部分数据都有更早的日期,否则这是非常无望的。如果日期很特殊,您可以创建一个计算的持久列来总体上加快查询速度。但是,我怀疑情况是否如此。
我可以为这样表述的查询设想一个更好的执行计划:
SELECT TOP (10) A.ColumnValue AS ValueA
FROM TableA A
WHERE EXISTS (SELECT 1 FROM TableB b WHERE A.ID = B.ID) AND
A.DateValue > '1982-05-02'
ORDER BY ValueA;
在 TableA(ValueA, DateValue, Id, ColumnValue)
和 TableB(id)
上有一个索引。该执行计划将从头开始扫描索引,然后在 DateValue
和 Id
和 return ColumnValue
上针对相应的匹配行进行测试。
不过,我认为SQL服务器不会生成这个计划(尽管值得一试),如果不生成我不知道如何强制它。
你说
When I remove the TOP or the ORDER BY ... it executes faster
这表明 SQL 服务器可以按所需顺序生成整个结果集。它只是在 TOP 10
的限制下变成梨形。这是 rowgoals 的常见问题。当 SQL 服务器知道您只需要前几个结果时,它可以选择一个不同的计划来尝试针对这种可能适得其反的情况进行优化。
较新的版本包含提示 DISABLE_OPTIMIZER_ROWGOAL
以在每个查询的基础上禁用此功能。在旧版本上,您可以使用 QUERYTRACEON 4138
,如下所示。
SELECT TOP (10) A.ColumnValue AS ValueA
FROM TableA AS A
INNER JOIN TableB AS B
ON A.ID = B.ID
WHERE A.DateValue > '1982-05-02'
ORDER BY ValueA
OPTION (QUERYTRACEON 4138)
您可以使用它来验证原因,但可能会发现 运行 QUERYTRACEON
的权限有问题。
在这种情况下,您可以隐藏变量中的 TOP
值,如下所示
DECLARE @Top INT = 10
SELECT TOP (@Top) A.ColumnValue AS ValueA
FROM TableA AS A
INNER JOIN TableB AS B
ON A.ID = B.ID
WHERE A.DateValue > '1982-05-02'
ORDER BY ValueA
option (optimize for (@Top = 1000000))