Рассмотрим эти две функции:
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 (сборка 7601: пакет обновления 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 (сборка 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
И то и другое
Как видите, у второго плана есть дополнительная сортировка. Заказы по B, A, C. Оптимизатор, по-видимому, недостаточно умен, чтобы понимать, что PARTITION BY B,A
это то же самое, что PARTITION BY A,B
и повторная сортировка данных.
Интересно, что в третьем запросе есть оба варианта, ROW_NUMBER
и нет дополнительной сортировки! План такой же, как и для первого запроса. (В проекте последовательности есть дополнительное выражение в списке вывода для дополнительного столбца, но нет дополнительной сортировки). Итак, в этом более сложном случае оптимизатор оказался достаточно умным, чтобы понять, что PARTITION BY B,A
это то же самое, что и PARTITION BY A,B
.
В первом и третьем запросах оператор сканирования индекса имеет свойство Ordered: 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);
тогда дополнительная сортировка появляется снова!
Может ли кто-нибудь пролить свет? Что здесь происходит в оптимизаторе?