Cara menulis kueri di SQL Server untuk menemukan nilai terdekat

16

Katakanlah saya memiliki nilai integer berikut dalam sebuah tabel

32
11
15
123
55
54
23
43
44
44
56
23

OK, daftarnya bisa berlanjut; itu tidak masalah. Sekarang saya ingin menanyakan tabel ini dan saya ingin mengembalikan sejumlah closest records. Katakanlah saya ingin mengembalikan 10 pertandingan rekaman terdekat ke angka 32. Dapatkah saya mencapai ini secara efisien?

Itu di SQL Server 2014.

MonsterMMORPG
sumber

Jawaban:

21

Dengan asumsi kolom diindeks berikut ini harus cukup efisien.

Dengan dua upaya 10 baris dan kemudian semacam (hingga) 20 dikembalikan.

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 

(yaitu berpotensi seperti di bawah ini)

masukkan deskripsi gambar di sini

Atau kemungkinan lain (yang mengurangi jumlah baris yang diurutkan hingga maks 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

masukkan deskripsi gambar di sini

NB: Rencana eksekusi di atas adalah untuk definisi tabel sederhana

CREATE TABLE [dbo].[YourTable](
    [YourCol] [int] NOT NULL CONSTRAINT [SomeIndex] PRIMARY KEY CLUSTERED 
)

Secara teknis, Sortir di cabang bawah tidak boleh diperlukan karena itu juga dipesan oleh Diff, dan dimungkinkan untuk menggabungkan dua hasil yang dipesan. Tetapi saya tidak bisa mendapatkan rencana itu.

Kueri memiliki ORDER BY Diff ASC, YourCol ASCdan bukan adil ORDER BY YourCol ASC, karena itulah yang akhirnya berfungsi untuk menyingkirkan Urutan di cabang teratas paket. Saya perlu menambahkan kolom sekunder di (meskipun itu tidak akan pernah mengubah hasilnya karena YourColakan sama untuk semua nilai dengan Diff yang sama) sehingga akan melalui gabungan gabungan (penggabungan) tanpa menambahkan Sort.

SQL Server tampaknya dapat menyimpulkan bahwa indeks pada X yang dicari dalam urutan naik akan menghasilkan baris yang dipesan oleh X + Y dan tidak diperlukan jenis. Tetapi tidak dapat menyimpulkan bahwa bepergian indeks dalam urutan menurun akan menghasilkan baris dalam urutan yang sama dengan YX (atau bahkan hanya unary minus X). Kedua cabang rencana menggunakan indeks untuk menghindari pengurutan, tetapi TOP 10di cabang bawah kemudian diurutkan berdasarkan Diff(meskipun mereka sudah dalam urutan itu) untuk mendapatkannya dalam urutan yang diinginkan untuk penggabungan.

Untuk definisi kueri / tabel lainnya, mungkin lebih sulit atau tidak mungkin untuk mendapatkan rencana penggabungan hanya dengan semacam cabang - karena bergantung pada menemukan ekspresi pemesanan yang SQL Server:

  1. Menerima bahwa pencarian indeks akan memasok pesanan yang ditentukan sehingga tidak diperlukan penyortiran sebelum bagian atas.
  2. Dengan senang hati digunakan dalam operasi gabungan sehingga tidak memerlukan pengurutan setelah TOP
Martin Smith
sumber
1

Saya agak bingung dan terkejut bahwa kita harus melakukan Union dalam kasus ini. Mengikuti adalah sederhana dan lebih efisien

SELECT TOP (@top) *
FROM @YourTable
ORDER BY ABS(YourCol-@x)

Berikut ini adalah kode lengkap dan rencana eksekusi yang membandingkan kedua pertanyaan

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 

Perbandingan rencana eksekusi

Pushkar Aditya
sumber
-3

Penyempurnaan saran kedua Martin:

WITH AB
     AS (SELECT *, ABS(32 - YourCol) AS Offset
         FROM   YourTable),
SELECT TOP 10 *
FROM   AB
ORDER  BY Offset ASC
20c
sumber
2
Ini mungkin kode yang sedikit lebih sederhana tetapi akan jauh lebih efisien. Kami bahkan dapat menggunakan SELECT TOP 10 * FROM YourTable ORDER BY ABS(YourCol - 32) ;Bahkan lebih sederhana. Juga tidak efisien.
ypercubeᵀᴹ