Для полей с автоинкрементом: MAX (ID) vs TOP 1 ID ORDER BY ID DESC
Я хочу найти максимальное значение AutoIncremented из поля. (он не извлекается после вставки, где я могу использовать и @@SCOPE_IDENTITY
т. д.) Какой из этих двух запросов будет выполняться быстрее или дает лучшую производительность.
Id
- это первичный ключ и autoincrement
поле для Table1
. И это для Sql Server 2005.
SELECT MAX(Id) FROM Table1
SELECT TOP 1 Id FROM Table1 ORDER BY Id DESC
[Edit]
Да, в данном случае Id
это поле, для которого я определил кластеризованный индекс.
Если это индекс, ID DESC
то какой ...
И да, было бы неплохо узнать, как на производительность повлияет
1. Id - это кластеризованный индекс + первичный ключ.
2. Id - это кластерный индекс, а не первичный ключ.
3. Id - это некластеризованный индекс ASC + первичный ключ.
4. Id - это некластеризованный индекс ASC, а не первичный ключ.
5. Id - это некластеризованный индекс DESC + первичный ключ.
6. Id - это некластеризованный индекс DESC, а не первичный ключ.
7. Идентификатор просто AutoIncrement
Надеюсь, это не сложная задача!
Ответов (7)7
Теоретически они будут использовать одни и те же планы и работать почти в одно и то же время.
На практике,
SELECT TOP 1 Id FROM Table1 ORDER BY Id DESC
с большей вероятностью будет использовать PRIMARY KEY INDEX
.
Кроме того, этот столбец более расширяемый, если вы решите выбрать другой столбец вместе с id
.
Фактический план MAX()
гласит:
SELECT <- AGGREGATE <- TOP <- CLUSTERED INDEX SCAN
, а план TOP 1
говорит:
SELECT <- TOP <- CLUSTERED INDEX SCAN
, т.е. aggregate
опущено.
Агрегат здесь на самом деле ничего не сделает, так как есть только одна строка.
PS Как @Mehrdad Afshari
и @John Sansom
отмечалось, в неиндексированном поле MAX
это немного быстрее (конечно, не 20
раз, как говорит оптимизатор):
- 18 874 368 строк УСТАНОВИТЬ ЯЗЫК АНГЛИЙСКИЙ УСТАНОВИТЬ ВРЕМЯ СТАТИСТИКИ ВКЛ. ВКЛЮЧИТЬ СТАТИСТИКУ IO ПЕЧАТЬ 'МАКС' ВЫБРАТЬ МАКС. (Id) ОТ мастера ПЕЧАТЬ 'ТОП 1' ВЫБРАТЬ ВЕРХНИЙ 1 идентификатор ИЗ главного ЗАКАЗА ПО идентификатору DESC ПЕЧАТЬ 'МАКС' ВЫБРАТЬ МАКС. (Id) ОТ мастера ПЕЧАТЬ 'ТОП 1' ВЫБРАТЬ ВЕРХНИЙ 1 идентификатор ИЗ главного ЗАКАЗА ПО идентификатору DESC ПЕЧАТЬ 'МАКС' ВЫБРАТЬ МАКС. (Id) ОТ мастера ПЕЧАТЬ 'ТОП 1' ВЫБРАТЬ ВЕРХНИЙ 1 идентификатор ИЗ главного ЗАКАЗА ПО идентификатору DESC ПЕЧАТЬ 'МАКС' ВЫБРАТЬ МАКС. (Id) ОТ мастера ПЕЧАТЬ 'ТОП 1' ВЫБРАТЬ ВЕРХНИЙ 1 идентификатор ИЗ главного ЗАКАЗА ПО идентификатору DESC ПЕЧАТЬ 'МАКС' ВЫБРАТЬ МАКС. (Id) ОТ мастера ПЕЧАТЬ 'ТОП 1' ВЫБРАТЬ ВЕРХНИЙ 1 идентификатор ИЗ главного ЗАКАЗА ПО идентификатору DESC Изменены языковые настройки на us_english. Время выполнения SQL Server: Процессорное время = 0 мс, прошедшее время = 1 мс. Время выполнения SQL Server: Процессорное время = 0 мс, прошедшее время = 1 мс. Время выполнения SQL Server: Процессорное время = 0 мс, прошедшее время = 1 мс. МАКСИМУМ Время выполнения SQL Server: Время ЦП = 0 мс, затраченное время = 20 мс. (строк обработано: 1) Стол "хозяин". Счетчик сканирования 3, логических чтений 32655, физических чтений 0, упреждающих чтений 447, lob-логических чтений 0, lob-физических чтений 0, lob-операций чтения 0. Время выполнения SQL Server: Процессорное время = 5452 мс, затраченное время = 2766 мс. ТОП 1 Время выполнения SQL Server: Процессорное время = 0 мс, прошедшее время = 1 мс. (строк обработано: 1) Стол "хозяин". Счетчик сканирования 3, логических чтений 32655, физических чтений 0, упреждающих чтений 2, логических чтений lob 0, физических чтений lob 0, упреждающих чтений lob 0. Время выполнения SQL Server: Время процессора = 6813 мс, затраченное время = 3449 мс. МАКСИМУМ Время выполнения SQL Server: Процессорное время = 0 мс, прошедшее время = 1 мс. (строк обработано: 1) Стол "хозяин". Счетчик сканирования 3, логических чтений 32655, физических чтений 0, упреждающих чтений 44, логических чтений lob 0, физических чтений lob 0, упреждающих чтений lob 0. Время выполнения SQL Server: Время процессора = 5359 мс, затраченное время = 2714 мс. ТОП 1 Время выполнения SQL Server: Процессорное время = 0 мс, прошедшее время = 1 мс. (строк обработано: 1) Стол "хозяин". Счетчик сканирований 3, логических чтений 32655, физических чтений 0, упреждающих чтений 0, логических чтений lob 0, физических чтений lob 0, упреждающих чтений lob 0. Время выполнения SQL Server: Время процессора = 6766 мс, затраченное время = 3379 мс. МАКСИМУМ Время выполнения SQL Server: Процессорное время = 0 мс, прошедшее время = 1 мс. (строк обработано: 1) Стол "хозяин". Счетчик сканирований 3, логических чтений 32655, физических чтений 0, упреждающих чтений 0, логических чтений lob 0, физических чтений lob 0, упреждающих чтений lob 0. Время выполнения SQL Server: Время процессора = 5406 мс, затраченное время = 2726 мс. ТОП 1 Время выполнения SQL Server: Процессорное время = 0 мс, прошедшее время = 1 мс. (строк обработано: 1) Стол "хозяин". Счетчик сканирований 3, логических чтений 32655, физических чтений 0, упреждающих чтений 0, логических чтений lob 0, физических чтений lob 0, упреждающих чтений lob 0. Время выполнения SQL Server: Время процессора = 6780 мс, затраченное время = 3415 мс. МАКСИМУМ Время выполнения SQL Server: Процессорное время = 0 мс, прошедшее время = 1 мс. (строк обработано: 1) Стол "хозяин". Счетчик сканирования 3, логических чтений 32655, физических чтений 0, упреждающих чтений 85, логических чтений lob 0, физических чтений lob 0, упреждающих чтений lob 0. Время выполнения SQL Server: Процессорное время = 5392 мс, затраченное время = 2709 мс. ТОП 1 Время выполнения SQL Server: Процессорное время = 0 мс, прошедшее время = 1 мс. (строк обработано: 1) Стол "хозяин". Счетчик сканирования 3, логических чтений 32655, физических чтений 0, упреждающих чтений 10, логических чтений lob 0, физических чтений lob 0, упреждающих чтений lob 0. Время выполнения SQL Server: Время процессора = 6766 мс, затраченное время = 3387 мс. МАКСИМУМ Время выполнения SQL Server: Процессорное время = 0 мс, прошедшее время = 1 мс. (строк обработано: 1) Стол "хозяин". Счетчик сканирований 3, логических чтений 32655, физических чтений 0, упреждающих чтений 0, логических чтений lob 0, физических чтений lob 0, упреждающих чтений lob 0. Время выполнения SQL Server: Время процессора = 5374 мс, затраченное время = 2708 мс. ТОП 1 Время выполнения SQL Server: Процессорное время = 0 мс, прошедшее время = 1 мс. (строк обработано: 1) Стол "хозяин". Счетчик сканирований 3, логических чтений 32655, физических чтений 0, упреждающих чтений 0, логических чтений lob 0, физических чтений lob 0, упреждающих чтений lob 0. Время выполнения SQL Server: Время процессора = 6797 мс, затраченное время = 3494 мс.
MAX
вообще быстрее.
Оба запроса будут использовать индекс столбца, если он существует.
Если для столбца нет индекса, TOP 1
запрос будет использовать Top N Sort
оператор для сортировки таблицы вместо агрегирования потока , что замедляет его работу.
MAX
также обеспечивает лучшую читаемость.
Боковое примечание : хотя MAX
в плане выполнения в индексированном случае будет использоваться оператор агрегирования потока, он не имеет каких-либо конкретных затрат, поскольку он просто обрабатывает одну строку ( Actual Rows = 1
). Вы можете сравнить запросы, запустив их в одном пакете, и увидеть относительную стоимость. В индексированном случае оба запроса будут стоить 50%. Я тестировал неиндексированный случай на таблице с примерно 7000 строками, и TOP будет стоить 65% по сравнению с MAX, который стоит 35%.
При наличии кластерного индекса практически нет разницы в производительности между двумя запросами.
Это потому, что оба будут выполнять сканирование кластеризованного индекса, которое будет нести 100% стоимости запроса.
Выполнение двух запросов к столбцу, не имеющему индекса, приводит к использованию 3 операторов в обоих планах выполнения.
Предложение Top использует оператор Sort, а функция Max использует оператор Stream Aggregate.
Когда индекса нет, функция MAX () обеспечивает лучшую производительность.
Здесь можно найти подтверждение концепции и полное описание тестового сценария:
Я только что протестировал два предоставленных вами оператора SQL на типичном наборе данных:
SELECT MAX(Id) FROM Table1
SELECT TOP 1 Id FROM Table1 ORDER BY Id DESC
И SELECT TOP 1 Id FROM Table1 ORDER BY Id DESC
немного быстрее, потому что у него на один шаг меньше в плане выполнения. Вот планы выполнения, которые выполняет каждый запрос:
ВЫБРАТЬ МАКС. (Id) ИЗ Таблица1
Сканирование кластерного индекса >> Вверх >> Агрегирование потоков >> Выбрать
ВЫБРАТЬ ИДЕНТИФИКАТОР ПЕРВОГО 1 ИЗ Таблицы 1 ПОРЯДОК ПО ИДЕНТИФИКАТОРУ УДАЛ.
Сканирование кластерного индекса >> Вверх >> Выбрать
Да, в этом случае Id - это поле, для которого я определил кластерный индекс. Если индекс - ID DESC, то что .. И да, было бы неплохо знать, как это повлияет на производительность, если
- Id - это кластерный индекс + первичный ключ.
- Id - это кластерный индекс, а не первичный ключ.
- Id - это некластеризованный индекс ASC + первичный ключ.
- Id - это некластеризованный индекс ASC, а не первичный ключ.
- Id - это некластеризованный индекс DESC + первичный ключ.
- Id - это некластеризованный индекс DESC, а не первичный ключ.
- Id - это просто AutoIncrement
Для случаев 1 и 2 оба будут выполнять сканирование кластерного индекса, которое возвращает одну запись. Между двумя запросами нет разницы в вводе-выводе.
Для случаев 3, 4, 5 и 6 оба будут выполнять сканирование индекса, которое возвращает одну запись. Между этими двумя запросами нет разницы в вводе-выводе.
В случае 7 оба будут выполнять сканирование таблицы. Нет никакой разницы в стоимости ввода-вывода.
Резюме: Кейсы 1-6 сделаны без выигрыша! Если вы находитесь в случае 7, то с точки зрения ввода-вывода вы уже проиграли.
Вы можете измерить ввод-вывод с помощью анализатора запросов SQL. Запустите это перед вашим запросом.
SET STATISTICS IO ON