Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

SQL Server chọn giá trị ngẫu nhiên (hoặc đầu tiên) với tổng hợp

Có một tổng hợp không có giấy tờ được gọi là ANY đó không phải là cú pháp hợp lệ nhưng có thể xuất hiện trong các kế hoạch thực thi của bạn. Tuy nhiên, điều này không mang lại bất kỳ lợi thế nào về hiệu suất.

Giả sử cấu trúc bảng và chỉ mục sau

CREATE TABLE T
(
id int identity primary key,
[group] char(1) 
)

CREATE NONCLUSTERED INDEX ix ON T([group])

INSERT INTO T
SELECT TOP 1000000 CHAR( 65 + ROW_NUMBER() OVER (ORDER BY @@SPID) % 3)
FROM sys.all_objects o1, sys.all_objects o2, sys.all_objects o3

Tôi cũng đã điền dữ liệu mẫu sao cho có nhiều hàng cho mỗi nhóm.

Truy vấn ban đầu của bạn

SELECT MAX(id),
       [group]
FROM   T
GROUP  BY [group]  

Cung cấp Table 'T'. Scan count 1, logical reads 1367 và kế hoạch

  |--Stream Aggregate(GROUP BY:([[T].[group]) DEFINE:([Expr1003]=MAX([[T].[id])))
       |--Index Scan(OBJECT:([[T].[ix]), ORDERED FORWARD)

Viết lại để lấy ANY tổng hợp ...

;WITH cte AS
(
SELECT *,
        ROW_NUMBER() OVER (PARTITION BY [group] ORDER BY [group] ) AS RN
FROM T)
SELECT id,
       [group]
FROM    cte     
WHERE RN=1

Cung cấp Table 'T'. Scan count 1, logical reads 1367 và kế hoạch

  |--Stream Aggregate(GROUP BY:([[T].[group]) DEFINE:([[T].[id]=ANY([[T].[id])))
       |--Index Scan(OBJECT:([[T].[ix]), ORDERED FORWARD)

Mặc dù SQL Server có khả năng có thể ngừng xử lý nhóm ngay sau khi giá trị đầu tiên được tìm thấy và bỏ qua giá trị tiếp theo thì không. Nó vẫn xử lý tất cả các hàng và các lần đọc logic giống nhau.

Đối với ví dụ cụ thể này với nhiều hàng trong nhóm, phiên bản hiệu quả hơn sẽ là CTE đệ quy.

WITH    RecursiveCTE
AS      (
        SELECT TOP 1 id, [group]
        FROM T
        ORDER BY [group]
        UNION   ALL
        SELECT  R.id, R.[group]
        FROM    (
                SELECT  T.*,
                        rn = ROW_NUMBER() OVER (ORDER BY (SELECT 0))
                FROM    T
                JOIN    RecursiveCTE R
                        ON  R.[group] < T.[group]
                ) R
        WHERE   R.rn = 1
        )
SELECT  *
FROM    RecursiveCTE
OPTION  (MAXRECURSION 0);

Điều này mang lại cho

Table 'Worktable'. Scan count 2, logical reads 19
Table 'T'. Scan count 4, logical reads 12

Các lần đọc logic ít hơn nhiều vì nó truy xuất hàng đầu tiên trên mỗi nhóm sau đó tìm kiếm nhóm tiếp theo thay vì đọc một loạt các bản ghi không đóng góp vào kết quả cuối cùng.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Thêm một số hàng vào tập hợp kết quả của một truy vấn SQL

  2. SQL:Chuẩn hóa cơ sở dữ liệu trong khi giữ lại các ràng buộc

  3. Giải phẫu các bế tắc của SQL Server và cách tốt nhất để tránh chúng

  4. Các tham số đầu ra của thủ tục được lưu trữ trong SQL Server Profiler

  5. Tạo một dạng xem trong SQL Server 2017