Hãy xem xét hai chức năng này:
ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C)
ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C)
Theo tôi hiểu, họ tạo ra kết quả chính xác như nhau. Nói cách khác, thứ tự mà bạn liệt kê các cột trong PARTITION BY
mệnh đề không quan trọng.
Nếu có một chỉ số trên (A,B,C)
tôi dự kiến trình tối ưu hóa sẽ sử dụng chỉ số này trong cả hai biến thể.
Nhưng thật ngạc nhiên, trình tối ưu hóa đã quyết định thực hiện một Sắp xếp rõ ràng hơn trong biến thể thứ hai.
Tôi đã thấy nó trên SQL Server 2008 Standard và SQL Server 2014 Express.
Đây là một kịch bản đầy đủ mà tôi đã sử dụng để tái tạo nó.
Đã thử trên Microsoft SQL Server 2014 - 12.0.2000.8 (X64) 20 tháng 2 năm 2014 20:04:26 Bản quyền (c) Microsoft Corporation Express Edition (64-bit) trên Windows NT 6.1 (Build 7601: Gói dịch vụ 1)
và Microsoft SQL Server 2014 (SP1-CU7) (KB3162659) - 12.0.4459.0 (X64) 27 tháng 5 năm 2016 15:33:17 Bản quyền (c) Microsoft Corporation Express Edition (64-bit) trên Windows NT 6.1 (Bản dựng 7601: Dịch vụ Gói 1)
với cả Công cụ ước tính Cardinality cũ và mới bằng cách sử dụng OPTION (QUERYTRACEON 9481)
và OPTION (QUERYTRACEON 2312)
.
Thiết lập bảng, chỉ mục, dữ liệu mẫu
CREATE TABLE [dbo].[T](
[ID] [int] IDENTITY(1,1) NOT NULL,
[A] [int] NOT NULL,
[B] [int] NOT NULL,
[C] [int] NOT NULL,
CONSTRAINT [PK_T] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_ABC] ON [dbo].[T]
(
[A] ASC,
[B] ASC,
[C] ASC
)WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF,
DROP_EXISTING = OFF,
ONLINE = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON)
GO
INSERT INTO [dbo].[T] ([A],[B],[C]) VALUES
(10, 20, 30),
(10, 21, 31),
(10, 21, 32),
(10, 21, 33),
(11, 20, 34),
(11, 21, 35),
(11, 21, 36),
(12, 20, 37),
(12, 21, 38),
(13, 21, 39);
Truy vấn
SELECT -- AB
ID,A,B,C
,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
FROM T
ORDER BY C
OPTION(RECOMPILE);
SELECT -- BA
ID,A,B,C
,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
FROM T
ORDER BY C
OPTION(RECOMPILE);
SELECT -- both
ID,A,B,C
,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
FROM T
ORDER BY C
OPTION(RECOMPILE);
Kế hoạch thực hiện
PHẦN THAM GIA A, B
PHẦN THAM GIA B, A
Cả hai
Như bạn có thể thấy, kế hoạch thứ hai có thêm Sắp xếp. Nó đặt hàng bởi B, A, C. Trình tối ưu hóa, rõ ràng, không đủ thông minh để nhận ra đó PARTITION BY B,A
là giống PARTITION BY A,B
và sắp xếp lại dữ liệu.
Thật thú vị, truy vấn thứ ba có cả hai biến thể ROW_NUMBER
trong đó và không có Sắp xếp thêm! Kế hoạch tương tự như đối với truy vấn đầu tiên. (Dự án tuần tự có biểu thức bổ sung trong Danh sách đầu ra cho cột thêm, nhưng không có Sắp xếp thêm). Vì vậy, trong trường hợp phức tạp hơn này, trình tối ưu hóa dường như đủ thông minh để nhận ra điều đó PARTITION BY B,A
giống như PARTITION BY A,B
.
Trong các truy vấn thứ nhất và thứ ba, toán tử Index Scan có thuộc tính được đặt hàng: Đúng, trong truy vấn thứ hai, nó là Sai.
Thậm chí thú vị hơn, nếu tôi viết lại truy vấn thứ ba như thế này (hoán đổi hai cột):
SELECT -- both
ID,A,B,C
,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
FROM T
ORDER BY C
OPTION(RECOMPILE);
sau đó sắp xếp thêm xuất hiện một lần nữa!
Ai đó có thể làm sáng tỏ? Điều gì đang xảy ra trong trình tối ưu hóa ở đây?