Я сравниваю два запроса в SQL Server 2012. Цель состоит в том, чтобы использовать все соответствующие информацию , доступные из оптимизатора запросов при выборе наилучшего запроса. Оба запроса дают одинаковые результаты; максимальный заказ для всех клиентов.
Очистка пула буферов была выполнена перед выполнением каждого запроса с помощью FREEPROCCACHE и DROPCLEANBUFFERS
Используя информацию, представленную ниже, какой запрос является лучшим выбором?
-- Query 1 - return the maximum order id for a customer
SELECT orderid, custid
FROM Sales.Orders AS O1
WHERE orderid = (SELECT MAX(O2.orderid)
FROM Sales.Orders AS O2
WHERE O2.custid = O1.custid);
-- Query 2 - return the maximum order id for a customer
SELECT MAX(orderid), custid
FROM Sales.Orders AS O1
group by custid
order by custid
ВРЕМЯ СТАТИСТИКИ
Запрос 1 STATISTICS TIME: время процессора = 0 мс, прошедшее время = 24 мс
Запрос 2 STATISTICS TIME: время процессора = 0 мс, прошедшее время = 23 мс
СТАТИСТИКА IO
Запрос 1 STATISTICS IO: Таблица «Заказы». Сканирование 1, логическое чтение 5, физическое чтение 2, чтение с опережением 0, логическое чтение с бита 0, физическое чтение с бита 0, чтение с опережением чтения 0.
Запрос 2 STATISTICS IO: Таблица «Заказы». Сканирование 1, логическое чтение 4, физическое чтение 1, чтение с опережением 8, логическое чтение с бита 0, физическое чтение с бита 0, чтение с опережением чтения 0.
Планы выполнения
ВЫБЕРИТЕ свойства Query 1
ВЫБЕРИТЕ свойства Query 2
Выводы:
Запрос 1
- Пакетная стоимость 48%
- Логические чтения 5
- Физические чтения 2
- Чтение впереди: 0
- Время процессора: 0 мс
- Прошедшее время 24 мс
- Ориентировочная стоимость поддерева: 0,0050276
- CompileCPU: 2
- CompileMemory: 384
- CompileTime: 2
Запрос 2
- Пакетная стоимость 52%
- Логические чтения 4
- Физические Чтения 1
- Чтение вперед: 8
- Процессорное время 0
- Прошедшее время 23мс
- Ориентировочная стоимость поддерева: 0,0054782
- CompileCPU: 0
- CompileMemory: 192
- CompileTime: 0
Лично, хотя Query 2 имеет более высокую стоимость пакета в соответствии с графическим планом, я думаю, что он более эффективен, чем Query 1. Это потому, что запрос 2 требует меньше логических чтений, имеет немного меньшее время, затрачиваемое на значения compilecpu, compilememory и compiletime. понизит. чтение с опережением - 8 для запроса 2 и 0 для запроса 1.
Обновление 12:03
Определение кластерного индекса
ALTER TABLE [Sales].[Orders] ADD CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED
(
[orderid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Некластерный индекс idx_nc_custid
CREATE NONCLUSTERED INDEX [idx_nc_custid] ON [Sales].[Orders]
(
[custid] 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) ON [PRIMARY]
GO
источник
Ответы:
Мне нравится ваш подход к тщательному рассмотрению вопросов настройки запросов и рассмотрения вариантов и планов. Я хотел бы, чтобы больше разработчиков сделали это. Одно предостережение: всегда проверяйте множество строк, просматривая логические чтения, это небольшая таблица. Попробуйте сгенерировать пример загрузки и повторите запрос. Одна небольшая проблема - в вашем верхнем запросе вы не запрашиваете заказ, в нижнем запросе вы находитесь. Вы должны сравнить и сопоставить их каждый с порядком.
Я только что быстро создал таблицу SalesOrders с 200 000 заказов на продажу, которая по-прежнему невелика для любого воображения. И запустил запросы с ORDER BY в каждом. Я тоже немного поиграл с индексами.
Без кластеризованного индекса для OrderID, просто некластеризованный индекс для CustID . Второй запрос . Особенно с заказом, включенным в каждый. В первом запросе было вдвое больше чтений, чем во втором запросе, а процент затрат между запросами составил 67% / 33%.
С кластеризованным индексом для OrderID и некластеризованным индексом только для CustID. Они выполнялись с одинаковой скоростью и точно таким же числом операций чтения.
Поэтому я бы посоветовал вам увеличить количество строк и провести еще несколько тестов. Но мой окончательный анализ ваших запросов -
Вы можете обнаружить, что они ведут себя более сходно, чем вы думаете, когда увеличиваете строки, так что помните об этом и проверяйте таким образом.
Если все, что вы когда-либо захотите вернуть, - это максимальный OrderID для каждого клиента, и вы хотите определить, что по OrderID, являющемуся наибольшим OrderID, то второй запрос из этих двух - лучший способ уйти от моего мышления - это немного Проще и немного дороже, в зависимости от стоимости поддерева, быстрее и проще расшифровать утверждение. Если вы намереваетесь когда-нибудь добавить другие столбцы в свой набор результатов? Тогда первый запрос позволяет вам сделать это.
Обновлено: один из ваших комментариев по вашему вопросу был:
Но лучший способ сделать это - проверить с большим количеством данных - всегда убедитесь, что у вас есть данные, согласующиеся с производством и ожидаемым будущим производством. Планы запросов начинают искать данные, когда вы добавляете больше строк в таблицы и пытаетесь сохранить распределение, которое вы ожидаете в рабочей среде. И обратите внимание на такие вещи, как «Заказать по» или «нет», здесь я не думаю, что в конечном итоге это будет иметь какое-то ужасное значение, но все же стоит окунуться в это.
Ваш подход сравнения этого уровня детализации и данных является хорошим. Стоимость поддерева в основном произвольна и бессмысленна, но все же стоит хотя бы взглянуть на сравнение между изменениями / изменениями или даже между запросами. Глядя на статистику времени и IO, очень важно, так же как и на план для всего, что кажется неуместным в отношении размера данных, с которыми вы работаете, и того, что вы пытаетесь сделать.
источник