Cách viết truy vấn trong SQL Server để tìm giá trị gần nhất


15

Hãy nói rằng tôi có các giá trị nguyên sau đây trong một bảng

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

OK, danh sách có thể tiếp tục; nó không thành vấn đề Bây giờ tôi muốn truy vấn bảng này và tôi muốn trả về một số lượng nhất định closest records. Giả sử tôi muốn trả lại 10 trận đấu kỷ lục gần nhất cho số 32. Tôi có thể đạt được điều này một cách hiệu quả không?

Đó là trong SQL Server 2014.

Câu trả lời:


21

Giả sử cột được lập chỉ mục sau đây sẽ có hiệu quả hợp lý.

Với hai lần tìm kiếm 10 hàng và sau đó một loại (tối đa) 20 được trả về.

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 

(tức là có khả năng giống như bên dưới)

nhập mô tả hình ảnh ở đây

Hoặc một khả năng khác (làm giảm số lượng hàng được sắp xếp tối đa 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

nhập mô tả hình ảnh ở đây

NB: Kế hoạch thực hiện ở trên là cho định nghĩa bảng đơn giản

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

Về mặt kỹ thuật, Sắp xếp trên nhánh dưới cùng không cần thiết vì điều đó cũng được đặt hàng bởi Diff và có thể hợp nhất hai kết quả được đặt hàng. Nhưng tôi đã không thể có được kế hoạch đó.

Truy vấn có ORDER BY Diff ASC, YourCol ASCvà không chỉ ORDER BY YourCol ASC, bởi vì đó là những gì đã kết thúc hoạt động để loại bỏ Sắp xếp trong nhánh trên cùng của kế hoạch. Tôi cần thêm cột thứ cấp vào (mặc dù nó sẽ không thay đổi kết quả như YourColnhau đối với tất cả các giá trị có cùng Diff) để nó đi qua phép nối hợp nhất (nối) mà không cần thêm Sắp xếp.

SQL Server dường như có thể suy ra rằng một chỉ mục trên X được tìm kiếm theo thứ tự tăng dần sẽ phân phối các hàng theo thứ tự X + Y và không cần sắp xếp. Nhưng không thể suy ra rằng việc di chuyển chỉ mục theo thứ tự giảm dần sẽ phân phối các hàng theo cùng thứ tự với YX (hoặc thậm chí chỉ đơn thuần trừ X). Cả hai nhánh của kế hoạch đều sử dụng một chỉ mục để tránh sắp xếp nhưng TOP 10nhánh ở nhánh dưới sau đó được sắp xếp theo Diff(mặc dù chúng đã theo thứ tự đó) để có được chúng theo thứ tự mong muốn cho việc hợp nhất.

Đối với các truy vấn / định nghĩa bảng khác, có thể khó hơn hoặc không thể có được kế hoạch hợp nhất chỉ với một loại của một nhánh - vì nó phụ thuộc vào việc tìm một biểu thức đặt hàng mà SQL Server:

  1. Chấp nhận rằng tìm kiếm chỉ mục sẽ cung cấp thứ tự được chỉ định để không cần sắp xếp trước khi đứng đầu.
  2. Rất vui khi sử dụng trong hoạt động hợp nhất vì vậy không yêu cầu sắp xếp sau TOP

1

Tôi hơi bối rối và ngạc nhiên khi chúng tôi phải làm Liên minh trong trường hợp này. Sau đây là đơn giản và hiệu quả hơn

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

Sau đây là mã hoàn chỉnh và kế hoạch thực hiện so sánh cả hai truy vấn

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 

So sánh kế hoạch thực hiện


-3

Sàng lọc đề nghị thứ hai của Martin:

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

2
Nó có thể là một mã đơn giản hơn một chút nhưng nó sẽ kém hiệu quả hơn nhiều. Chúng tôi thậm chí có thể sử dụng thậm SELECT TOP 10 * FROM YourTable ORDER BY ABS(YourCol - 32) ;chí đơn giản hơn. Cũng không hiệu quả.
ypercubeᵀᴹ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.