Для полей с автоинкрементом: 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)

Решение

Теоретически они будут использовать одни и те же планы и работать почти в одно и то же время.

На практике,

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 мс.

Просто сравните планы выполнения, и вы увидите (нажмите Ctrl+M в Management Studio при редактировании запроса). Я предполагаю, что эти запросы одинаково эффективны при наличии (кластерного) индекса в Id столбце.

Однако в целом это очень плохая идея.

MAX вообще быстрее.

Оба запроса будут использовать индекс столбца, если он существует.

Если для столбца нет индекса, TOP 1 запрос будет использовать Top N Sort оператор для сортировки таблицы вместо агрегирования потока , что замедляет его работу.

MAX также обеспечивает лучшую читаемость.

Боковое примечание : хотя MAX в плане выполнения в индексированном случае будет использоваться оператор агрегирования потока, он не имеет каких-либо конкретных затрат, поскольку он просто обрабатывает одну строку ( Actual Rows = 1 ). Вы можете сравнить запросы, запустив их в одном пакете, и увидеть относительную стоимость. В индексированном случае оба запроса будут стоить 50%. Я тестировал неиндексированный случай на таблице с примерно 7000 строками, и TOP будет стоить 65% по сравнению с MAX, который стоит 35%.

Никто не упомянул IDENT_CURRENT ('Table1') - их всех сдувает - конечно, он работает только с столбцами идентификаторов, но это был вопрос ...

При наличии кластерного индекса практически нет разницы в производительности между двумя запросами.

Это потому, что оба будут выполнять сканирование кластеризованного индекса, которое будет нести 100% стоимости запроса.

Выполнение двух запросов к столбцу, не имеющему индекса, приводит к использованию 3 операторов в обоих планах выполнения.

Предложение Top использует оператор Sort, а функция Max использует оператор Stream Aggregate.

Когда индекса нет, функция MAX () обеспечивает лучшую производительность.

Здесь можно найти подтверждение концепции и полное описание тестового сценария:

Сравнение производительности Top 1 Verses Функция 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, то что .. И да, было бы неплохо знать, как это повлияет на производительность, если

  1. Id - это кластерный индекс + первичный ключ.
  2. Id - это кластерный индекс, а не первичный ключ.
  3. Id - это некластеризованный индекс ASC + первичный ключ.
  4. Id - это некластеризованный индекс ASC, а не первичный ключ.
  5. Id - это некластеризованный индекс DESC + первичный ключ.
  6. Id - это некластеризованный индекс DESC, а не первичный ключ.
  7. Id - это просто AutoIncrement

Для случаев 1 и 2 оба будут выполнять сканирование кластерного индекса, которое возвращает одну запись. Между двумя запросами нет разницы в вводе-выводе.

Для случаев 3, 4, 5 и 6 оба будут выполнять сканирование индекса, которое возвращает одну запись. Между этими двумя запросами нет разницы в вводе-выводе.

В случае 7 оба будут выполнять сканирование таблицы. Нет никакой разницы в стоимости ввода-вывода.

Резюме: Кейсы 1-6 сделаны без выигрыша! Если вы находитесь в случае 7, то с точки зрения ввода-вывода вы уже проиграли.

Вы можете измерить ввод-вывод с помощью анализатора запросов SQL. Запустите это перед вашим запросом.

SET STATISTICS IO ON