Answers:
假设该列已建立索引,则以下内容应相当有效。
两次搜索10行,然后返回某种(最多)20行。
WITH CTE
AS ((SELECT TOP 10 *
FROM YourTable
WHERE YourCol > 32
ORDER BY YourCol ASC)
UNION ALL
(SELECT TOP 10 *
FROM YourTable
WHERE YourCol <= 32
ORDER BY YourCol DESC))
SELECT TOP 10 *
FROM CTE
ORDER BY ABS(YourCol - 32) ASC
(即可能类似于以下内容)
或另一种可能性(将排序的行数减少到最大10)
WITH A
AS (SELECT TOP 10 *,
YourCol - 32 AS Diff
FROM YourTable
WHERE YourCol > 32
ORDER BY Diff ASC, YourCol ASC),
B
AS (SELECT TOP 10 *,
32 - YourCol AS Diff
FROM YourTable
WHERE YourCol <= 32
ORDER BY YourCol DESC),
AB
AS (SELECT *
FROM A
UNION ALL
SELECT *
FROM B)
SELECT TOP 10 *
FROM AB
ORDER BY Diff ASC
注意:上面的执行计划是针对简单表定义的
CREATE TABLE [dbo].[YourTable](
[YourCol] [int] NOT NULL CONSTRAINT [SomeIndex] PRIMARY KEY CLUSTERED
)
从技术上讲,也不需要底部分支上的Sort,因为Diff也对它进行排序,并且可以合并两个排序的结果。但是我没有那个计划。
该查询具有ORDER BY Diff ASC, YourCol ASC
,而不仅仅是ORDER BY YourCol ASC
,因为最终摆脱了计划顶部分支中的“排序”。我需要在其中添加辅助列(即使它永远不会更改结果,因为YourCol
对于具有相同Diff的所有值都将是相同的),因此它将通过合并联接(并置)而不添加排序。
SQL Server似乎能够推断按升序查找的X索引将传递按X + Y排序的行,并且不需要排序。但是,无法推断出以降序移动索引将以与YX相同的顺序传递行(甚至只是一元减X)。计划的两个分支都使用索引来避免排序,但是对TOP 10
底部分支中的进行排序Diff
(即使它们已经按该顺序排序),以使其按合并所需的顺序进行排序。
对于其他查询/表定义,仅通过一个分支即可获得合并计划可能比较棘手,甚至无法实现-因为它依赖于查找SQL Server的排序表达式:
TOP
对于在这种情况下必须进行联盟,我感到有些困惑和惊讶。跟随简单而高效
SELECT TOP (@top) *
FROM @YourTable
ORDER BY ABS(YourCol-@x)
以下是比较这两个查询的完整代码和执行计划
DECLARE @YourTable TABLE (YourCol INT)
INSERT @YourTable (YourCol)
VALUES (32),(11),(15),(123),(55),(54),(23),(43),(44),(44),(56),(23)
DECLARE @x INT = 100, @top INT = 5
--SELECT TOP 100 * FROM @YourTable
SELECT TOP (@top) *
FROM @YourTable
ORDER BY ABS(YourCol-@x)
;WITH CTE
AS ((SELECT TOP 10 *
FROM @YourTable
WHERE YourCol > 32
ORDER BY YourCol ASC)
UNION ALL
(SELECT TOP 10 *
FROM @YourTable
WHERE YourCol <= 32
ORDER BY YourCol DESC))
SELECT TOP 10 *
FROM CTE
ORDER BY ABS(YourCol - 32) ASC
SELECT TOP 10 * FROM YourTable ORDER BY ABS(YourCol - 32) ;
更简单的方法。也不高效。