Подтвердить что ты не робот

Производительность SQL MAX()

Только что получил небольшой вопрос. При попытке получить одно максимальное значение таблицы. Какой из них лучше?

SELECT MAX(id) FROM myTable WHERE (whatever)

или

SELECT TOP 1 id FROM myTable WHERE (whatever) ORDER BY id DESC

Я использую Microsoft SQL Server 2012

4b9b3361

Ответ 1

Не будет никакой разницы, поскольку вы можете проверить себя, проверив планы выполнения. Если id - это кластеризованный индекс, вы должны увидеть упорядоченное сканирование индексированных кластеров; если он не проиндексирован, вы все равно увидите либо сканирование таблицы, либо кластерное сканирование индекса, но оно не будет упорядочено в любом случае.

Подход TOP 1 может быть полезен, если вы хотите вытащить другие значения из строки, что проще, чем потянуть max в подзапрос и затем присоединиться. Если вам нужны другие значения из строки, вам нужно диктовать, как обращаться со связями в обоих случаях.

Сказав это, есть некоторые сценарии, в которых план может быть другим, поэтому важно проверить в зависимости от того, индексирован ли столбец и монотонно ли он увеличивается. Я создал простую таблицу и вставил 50000 строк:

CREATE TABLE dbo.x
(
  a INT, b INT, c INT, d INT, 
  e DATETIME, f DATETIME, g DATETIME, h DATETIME
);
CREATE UNIQUE CLUSTERED INDEX a ON dbo.x(a);
CREATE INDEX b ON dbo.x(b)
CREATE INDEX e ON dbo.x(e);
CREATE INDEX f ON dbo.x(f);

INSERT dbo.x(a, b, c, d, e, f, g, h)
SELECT 
  n.rn, -- ints monotonically increasing
  n.a,  -- ints in random order
  n.rn, 
  n.a, 
  DATEADD(DAY, n.rn/100, '20100101'), -- dates monotonically increasing
  DATEADD(DAY, -n.a % 1000, '20120101'),     -- dates in random order
  DATEADD(DAY, n.rn/100, '20100101'),
  DATEADD(DAY, -n.a % 1000, '20120101')
FROM
(
  SELECT TOP (50000) 
     (ABS(s1.[object_id]) % 10000) + 1, 
     rn = ROW_NUMBER() OVER (ORDER BY s2.[object_id])
  FROM sys.all_objects AS s1 
  CROSS JOIN sys.all_objects AS s2
) AS n(a,rn);
GO

В моей системе это создало значения в a/c от 1 до 50000, b/d между 3 и 9994, e/g с 2010-01-01 по 2011-05-16 и f/h с 2009-04 -28 до 2012-01-01.

Сначала сравним индексированные монотонно возрастающие целые столбцы a и c. a имеет кластерный индекс, c не:

SELECT MAX(a) FROM dbo.x;
SELECT TOP (1) a FROM dbo.x ORDER BY a DESC;

SELECT MAX(c) FROM dbo.x;
SELECT TOP (1) c FROM dbo.x ORDER BY c DESC;

Результаты:

enter image description here

Большая проблема с 4-м запросом заключается в том, что, в отличие от MAX, для него требуется сортировка. Здесь 3 по сравнению с 4:

enter image description here

enter image description here

Это будет распространенной проблемой во всех этих вариантах запроса: a MAX против необработанного столбца сможет копировать сканирование кластерного индекса и выполнять агрегацию потока, тогда как TOP 1 необходимо выполнить сортировка, которая будет дороже.

Я проверил и увидел те же самые результаты при тестировании b + d, e + g и f + h.

Итак, мне кажется, что помимо создания большего количества кода соответствия стандартам существует потенциальная эффективность использования MAX в пользу TOP 1 в зависимости от базовой таблицы и индексов (которая может измениться после вас 'выложите свой код в производство). Поэтому я бы сказал, что без дополнительной информации MAX предпочтительнее.

(И, как я уже говорил, TOP 1 действительно может быть поведением, которое вам нужно, если вы тянете дополнительные столбцы. Вам нужно будет протестировать методы MAX + JOIN, если это вы после.)

Ответ 2

Первый, безусловно, яснее в намерении.

Не должно быть существенных различий в производительности для этого конкретного запроса (они должны быть практически идентичны, хотя результат отличается, если в myTable нет строк). Если у вас нет веских причин для настройки запроса (например, доказанной проблемы с производительностью), всегда выбирайте тот, который показывает намерение кода.

Ответ 3

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

Ответ 4

Хотя я подозреваю, что оператор сортировки TOP 1 превышен в плане. Я пробовал с ТОП-1, ТОП-100, > и ТОП-101, и все дали мне такую ​​же расчетную стоимость поддерева, несмотря на то, что последнему нужно было отсортировать все строки. - Мартин Смит 2 июля в 6:53

Если вам нужен 1 ряд или 100 строк, оптимизатор должен выполнить такой же объем работы в этом примере, то есть прочитать все строки из таблицы (сканирование с кластерным индексом). Затем отсортировать все эти строки (сортировать оперант), поскольку нет индекс в столбце C..Наконец просто покажите, какой из них необходим.

SELECT TOP (1) b FROM dbo.x ORDER BY b DESC
option(recompile); 
SELECT TOP (100) b FROM dbo.x ORDER BY b DESC
option(recompile); 

Попробуйте код выше, и здесь top 1 и top 100 показывают стоимость diff, потому что есть индекс в столбце b. Таким образом, в этом случае вам не нужно читать все строки и сортировать их, но работа заключается в том, чтобы перейти к указателю последней страницы. Для одной строки прочитайте последнюю строку на последней странице листа индекса. TFor 100 row найдите последнюю строку на последней странице, а затем запустите обратное сканирование, пока не получите 100 строк.