En yakın değerleri bulmak için SQL Server'da bir sorgu yazma


16

Diyelim ki bir tabloda aşağıdaki tamsayı değerlerim var

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

Tamam, liste devam edebilir; önemli değil. Şimdi bu tabloyu sorgulamak istiyorum ve belirli bir sayı döndürmek istiyorum closest records. Diyelim ki en yakın 10 rekoru 32 numarasına geri döndürmek istiyorum. Bunu verimli bir şekilde başarabilir miyim?

SQL Server 2014'te.

Yanıtlar:


21

Sütunun endekslendiği varsayıldığında, aşağıdakilerin makul derecede verimli olması gerekir.

10 sıralı iki arayış ve sonra bir tür (en fazla) 20 geri döndü.

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 

(yani potansiyel olarak aşağıdaki gibi bir şey)

resim açıklamasını buraya girin

Veya başka bir olasılık (sıralanan satır sayısını maksimum 10'a düşürür)

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

resim açıklamasını buraya girin

Not: Yukarıdaki yürütme planı basit tablo tanımı içindi

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

Teknik olarak, Diff tarafından sipariş edildiği gibi, alt daldaki Sıralama'ya da gerek duyulmamalıdır ve sipariş edilen iki sonucu birleştirmek mümkün olacaktır. Ama bu planı alamadım.

Sorgu ORDER BY Diff ASC, YourCol ASCsadece değil ORDER BY YourCol ASC, çünkü planın üst dalındaki Sırala'dan kurtulmak için çalışan şeydi. Ben ( YourColaynı Diff tüm değerleri için aynı olacak gibi sonucu hiç değişmeyecek olsa bile) ikincil sütun eklemek gerekiyordu, bu yüzden bir sıralama eklemeden birleştirme birleştirme (birleştirme) geçecekti.

SQL Server, artan sırada aranan X üzerindeki bir dizinin X + Y tarafından sıralanan satırları teslim edeceğini ve herhangi bir tür gerekli olmadığını çıkarabilir gibi görünüyor. Ancak, indeksi azalan sırada seyahat etmenin satırları YX ile aynı sırada (veya yalnızca tekli eksi X) ileteceğini çıkaramaz. Planın her iki dalı da bir sıralamayı önlemek için bir dizin kullanır, ancak TOP 10alt dalda Diffbirleştirme için istenen sırayla elde etmek üzere (zaten bu sırada olmalarına rağmen) sıralanır.

Diğer sorgular / tablo tanımları için daha karmaşık olabilir veya bir çeşit planla birleştirme planını elde etmek mümkün olmayabilir - SQL Server'ın bir sipariş ifadesi bulmaya dayanır:

  1. Dizin aramasının belirtilen sırayı vereceğini kabul eder, böylece üst kısımdan önce sıralama gerekmez .
  2. Birleştirme işleminde kullanmaktan mutluluk duyar, bu nedenle TOP

1

Biraz şaşkınım ve bu durumda Birlik yapmamız gerektiğine şaşırdım. Aşağıdakiler basit ve daha verimlidir

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

Her iki sorguyu karşılaştıran tam kod ve yürütme planı aşağıdadır

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 

Uygulama planı karşılaştırması


-3

Martin'in ikinci önerisinin iyileştirilmesi:

WITH AB
     AS (SELECT *, ABS(32 - YourCol) AS Offset
         FROM   YourTable),
SELECT TOP 10 *
FROM   AB
ORDER  BY Offset ASC

2
Biraz daha basit bir kod olabilir, ancak çok daha az verimli olacaktır. Daha da SELECT TOP 10 * FROM YourTable ORDER BY ABS(YourCol - 32) ;basit kullanabiliriz . Verimli de değil.
ypercubeᵀᴹ
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.