テーブルに次の整数値があるとしましょう
32
11
15
123
55
54
23
43
44
44
56
23
OK、リストは続きます。それは問題ではありません。次に、このテーブルにクエリを実行し、特定の数のclosest records
を返します。 数値32に最も近い10件のレコード一致を返したいこれを効率的に達成できますか?
SQL Server 2014に含まれています。
列にインデックスが付けられていると仮定すると、以下はかなり効率的です。
10行の2回のシークと、一種の(最大)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
)
技術的には、一番下のブランチでの並べ替えも必要ありません。これもDiffによって順序付けされているため、順序付けされた2つの結果をマージすることが可能です。しかし、私はその計画を得ることができませんでした。
クエリにはORDER BY Diff ASC, YourCol ASC
だけでなくORDER BY YourCol ASC
も含まれています。これは、プランの一番上のブランチでSortを取り除くために機能したためです。 (YourCol
が同じDiffのすべての値で同じになるため、結果が変更されることはありませんが)セカンダリ列を追加する必要があったため、マージ結合(連結)を行わずにソートを追加します。
SQL Serverは、昇順でシークされたXのインデックスはX + Yで並べられた行を提供し、ソートは不要であると推測できるようです。ただし、降順でインデックスを移動すると、行がY-X(または単項マイナスX)と同じ順序で配信されると推測することはできません。プランの両方のブランチはインデックスを使用してソートを回避しますが、下部のブランチのTOP 10
はDiff
によってソートされ(それらがすでにこの順序になっている場合でも)、目的の順序で取得されますマージのため。
他のクエリ/テーブル定義の場合、SQL Serverの順序付け式の検索に依存しているため、1つのブランチだけでマージプランを取得するのは難しい場合があります。
TOP
の後にソートする必要はありません。この場合、Unionを実行する必要があることに少し戸惑い、驚いています。以下はシンプルでより効率的です
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