Розглянемо ці дві функції:
ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C)
ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C)
Наскільки я розумію, вони дають точно такий же результат. Іншими словами, порядок, в якому ви перераховуєте стовпці в PARTITION BY
пункті, не має значення.
Якщо є індекс на, (A,B,C)
я очікував, що оптимізатор буде використовувати цей індекс в обох варіантах.
Але, на диво, оптимізатор вирішив зробити додатковий явний Сортування у другому варіанті.
Я бачив це на SQL Server 2008 Standard та SQL Server 2014 Express.
Ось повний сценарій, який я використовував для його відтворення.
Пробували на Microsoft SQL Server 2014 - 12.0.2000.8 (X64) 20 лютого 2014 20:04:26 Авторські права (c) Microsoft Corporation Express Edition (64-розрядна) для Windows NT 6.1 (Build 7601: Service Pack 1)
та Microsoft SQL Server 2014 (SP1-CU7) (KB3162659) - 12.0.4459.0 (X64) 27 травня 2016 15:33:17 Авторські права (c) Microsoft Corporation Express Edition (64-розрядна) для Windows NT 6.1 (Build 7601: Сервіс Пакет 1)
зі старим і новим Оцінювачем кардинальності за допомогою OPTION (QUERYTRACEON 9481)
та OPTION (QUERYTRACEON 2312)
.
Налаштуйте дані таблиці, індексу, вибірки
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);
Запити
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);
Плани виконання
РОЗДІЛ А, Б
ПАРТІЯ В, А
І те й інше
Як бачимо, другий план має додатковий сортування. Він замовляє B, A, C. Оптимізатор, мабуть, недостатньо розумний, щоб зрозуміти, що PARTITION BY B,A
це те саме, що PARTITION BY A,B
переробляє дані.
Цікаво, що в третьому запиті є обидва варіанти ROW_NUMBER
його, і додаткового сортування немає! План такий же, як і для першого запиту. (Проект «Послідовність» має додаткове вираження у списку вихідних даних для додаткового стовпця, але немає додаткового сортування). Отже, у цьому більш складному випадку оптимізатор виявився досить розумним, щоб зрозуміти, що PARTITION BY B,A
це те саме PARTITION BY A,B
.
У першому та третьому запитах оператор Index Scan має властивість упорядковано: True, у другому запиті - False.
Ще цікавіше, якщо я перепишу третій запит на зразок цього (поміняйте два стовпці):
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);
тоді знову з'являється додатковий Сортування!
Чи може хтось пролити світло? Що тут відбувається в оптимізаторі?