Порівняння двох запитів у SQL Server 2012


14

Я порівнюю два запити в 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 СТАТИСТИКА ЧАС: час процесора = 0 мс, минулий час = 24 мс

Запит 2 СТАТИСТИКА ВРЕМЯ: час процесора = 0 мс, минулий час = 23 мс

СТАТИСТИКА ІО

Запит 1 СТАТИСТИКА IO: Таблиця "Замовлення". Кількість сканувань 1, логічне зчитування 5, фізичне зчитування 2, зчитування вперед-зчитування 0, логічне зчитування лобі 0, лобічне фізичне зчитування 0, лобічне зчитування попереднє зчитування 0.

Запит 2 СТАТИСТИКА IO: Таблиця "Замовлення". Кількість сканувань 1, логічне зчитування 4, фізичне зчитування 1, зчитування вперед-зчитування 8, логічне зчитування лобі 0, лобічне фізичне зчитування 0, лобічне зчитування вперед-0.

Плани виконання

введіть тут опис зображення

SELECT властивості Запит 1

введіть тут опис зображення

SELECT властивості Запит 2

введіть тут опис зображення

Висновки:

Запит 1

  1. Вартість партії 48%
  2. Логічні читання 5
  3. Фізичні читання 2
  4. Читання вперед: 0
  5. Час процесора: 0 мс
  6. Час минув 24 мс
  7. Орієнтовна вартість піддерева: 0,0050276
  8. КомпіляціяCPU: 2
  9. Пам'ять компіляції: 384
  10. Час компіляції: 2

Запит 2

  1. Вартість партії - 52%
  2. Логічні читання 4
  3. Фізичні читання 1
  4. Читання наперед: 8
  5. Час процесора 0
  6. Час минув 23 мс
  7. Орієнтовна вартість піддерева: 0,0054782
  8. СкладітьCPU: 0
  9. Пам'ять компіляції: 192
  10. Час компіляції: 0

Особисто, навіть якщо запит 2 має більш високу пакетну вартість відповідно до графічного плану, я вважаю його більш ефективним, ніж запит 1. Це тому, що запит 2 вимагає менш логічного читання, має трохи менший минулий час, значення збирання, збірника та компіляції є нижній. попередні читання - 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

Коментарі не для розширеного обговорення; ця розмова була переміщена до чату .
Пол Білий 9

Відповіді:


10

Мені подобається ваш підхід до уважного розгляду налаштування запитів та перегляду варіантів та планів. Я б хотів, щоб це зробили більше розробників. Одна обережність була б - завжди тестуйте з великою кількістю рядків, дивлячись на логічні показання, це невелика таблиця. Спробуйте створити зразок завантаження та запустіть запит ще раз. Одне невелике питання - у своєму головному запиті ви не просите замовлення, у нижньому запиті ви є. Слід порівняти та порівняти їх із замовленням.

Я просто швидко створив таблицю SalesOrders з 200 000 замовлень на продаж - все ще не величезний у будь-якому розрізі уяви. І виконував запити за допомогою ORDER BY у кожному. Я теж трохи грав з індексами.

Без кластерного індексу в OrderID, просто некластеризований індекс на CustID . Другий запит випереджав. Особливо з замовленням, включеним у кожен. У першому запиті було прочитано вдвічі більше, ніж у другому, а відсоткові витрати складали 67% / 33% між запитами.

З кластеризованим індексом на OrderID та некластеризованим індексом лише на CustID Вони виконували з однаковою швидкістю та точно такою ж кількістю прочитаних.

Тому я б запропонував вам збільшити кількість рядків і зробити ще кілька тестувань. Але мій остаточний аналіз ваших запитів -

Ви можете виявити, що вони поводяться більш схоже, ніж ви усвідомлюєте, коли збільшуєте рядки, тому пам’ятайте про це застереження і перевіряйте це.

Якщо все, що ви хочете повернути, - це максимальний OrderID для кожного Клієнта, і ви хочете визначити, що найвищий OrderID - найбільший спосіб замовлення, то другий запит із цих двох - найкращий спосіб перейти з мого мислення - це небагато простіший і хоча дещо дорожчий на основі вартості піддерева, це швидше і простіше розшифрувати заяву. Якщо ви хочете коли-небудь додати інші стовпці у свій набір результатів? Тоді перший запит дозволяє вам це зробити.

Оновлено: Один із ваших коментарів під вашим запитанням:

Будь ласка, майте на увазі, що пошук найкращого запиту в цьому питанні - це засіб вдосконалення методів їх порівняння.

Але найкраще зайняти тестування з більшою кількістю даних - завжди гарантує, що у вас є дані, що відповідають виробництву та очікуваному майбутньому виробництву. Плани запитів почнуть шукати дані, коли ви додасте більше рядків до таблиць, і намагайтеся зберегти розподіл того, що ви очікували у виробництві. І зверніть увагу на такі речі, як, зокрема, замовлення "По чи ні", тут я не думаю, що це зрештою має жахливу різницю, але все-таки варто заглибитися.

Ваш підхід порівняння цього рівня деталізації та даних є хорошим. Витрати на піддерево є в основному довільними і безглуздими, але все-таки варто принаймні переглянути порівняння між правками / змінами або навіть між запитами. Дивлячись на статистику часу та IO є досить важливими, як і перегляд плану всього того, що відчуває себе непотрібним для розміру даних, з якими ви працюєте, і що ви намагаєтесь зробити.


Привіт ще раз, дякую за ваші пункти щодо використання більшого обсягу даних. Це не перший раз, коли хтось виховував це. Останній раз, хоча було розглянути можливу фрагментацію від розбивки сторінки. У своєму зразку з 200 000 рядків ви перевіряли фрагментацію?
Крейг Ефрейн

Добре, що у моєму маленькому швидкому прикладі рядків 200 тисяч я не зосереджувався на фрагментації, ні. Але так, як я це зробив, не було б. Я створив таблицю, заповнив її, а потім зробив індекси. Отже, вони були щойно створеними індексами. І це не змінить підходу до перегляду планів запитів, що, здається, є головним питанням. Об'єм даних великий - дійсно великий - для точного перегляду планів запитів. Я часто бачив випадки, коли це виглядало чудово у програмі Dev (з 1-10 рядками) і було жахливо в продажі з реальними даними. Але ваш підхід хороший, і сподіваємось, ця інформація та розмова в коментарях допомагає
Майк Уолш

Оскільки ми групуємось по custid, як ти зробив значення кустиду досить випадковими? Одне, що я пам’ятаю з своїх прочитань, - це важливість різних цінностей. Якби у custid була лише невелика кількість різних клієнтів, то вартість поточного агрегату була б нереальною.
Крейг Ефрейн

Я просто використав функцію RAND, щоб створити 100 клієнтів і випадковим чином призначити по одному замовленняID. Я робив швидку перевірку. :)
Майк Уолш

Дякую Майку за всю вашу допомогу. Останнє питання, хоча. На екранах властивостей SELECT Плану виконання 2012 року, які я вказав у своєму питанні, на які значення ви звертаєте увагу?
Крейг Ефрейн
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.