วิธีการเขียนแบบสอบถามใน SQL Server เพื่อค้นหาค่าที่ใกล้ที่สุด


16

สมมติว่าฉันมีค่าจำนวนเต็มต่อไปนี้ในตาราง

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

ตกลงรายการสามารถดำเนินต่อไป; มันไม่สำคัญ ตอนนี้ฉันต้องการสืบค้นตารางนี้และฉันต้องการคืนค่าจำนวนclosest recordsหนึ่ง สมมติว่าฉันต้องการคืนการแข่งขันที่ใกล้เคียงที่สุด 10 รายการให้กับตัวเลข 32ฉันสามารถทำสิ่งนี้ได้อย่างมีประสิทธิภาพหรือไม่

มันอยู่ใน SQL Server 2014

คำตอบ:


21

สมมติว่ามีการจัดทำดัชนีคอลัมน์ต่อไปนี้ควรมีประสิทธิภาพพอสมควร

ด้วยการค้นหาสองแถว 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 
)

ในทางเทคนิคแล้วการเรียงลำดับที่สาขาด้านล่างไม่จำเป็นเช่นกันเนื่องจาก 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:

  1. ยอมรับว่าการค้นหาดัชนีจะจัดหาคำสั่งที่ระบุดังนั้นไม่จำเป็นต้องเรียงลำดับก่อนด้านบน
  2. มีความสุขที่จะใช้ในการดำเนินการผสานจึงไม่ต้องเรียงลำดับหลังจาก TOP

1

ฉันสับสนและประหลาดใจเล็กน้อยที่เราต้องทำ 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 

การเปรียบเทียบแผนดำเนินการ


-3

การปรับแต่งคำแนะนำที่สองของ Martin:

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

2
อาจเป็นรหัสที่ง่ายกว่าเล็กน้อย แต่มีประสิทธิภาพน้อยกว่ามาก เราสามารถใช้SELECT TOP 10 * FROM YourTable ORDER BY ABS(YourCol - 32) ;ง่ายยิ่งขึ้น ไม่มีประสิทธิภาพเช่นกัน
ypercubeᵀᴹ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.