Розраховане поле SQL як у пункті SELECT, так і GROUP BY


11

Часто під час запитів моїх баз даних MS SQL Server мені потрібно створити обчислене поле, таке як це

(CASE WHEN A.type = 'Workover' THEN 'Workover' 
      ELSE (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' 
                 WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' 
                 WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' 
                 ELSE 'Other' 
            END)
END)

і тоді мені потрібно згрупувати свої результати за цим розрахованим полем (серед інших). Отже, у мене однаковий розрахунок як у пунктах SELECT, так і GROUP BY. Чи справді SQL-сервер виконує ці обчислення двічі, чи достатньо розумний це зробити лише один раз?

Відповіді:


13

У мене однаковий розрахунок як у пунктах SELECT, так і GROUP BY. Чи справді SQL-сервер виконує ці обчислення двічі, чи достатньо розумний це зробити лише один раз?

Проста відповідь полягає в тому, що SQL Server не дає ніяких загальних гарантій щодо того, коли і скільки разів скалярне вираження буде оцінено під час виконання.

Існують всілякі складні (і недокументовані) поведінки в оптимізаторі та механізмі виконання щодо розміщення, виконання та кешування скалярних виразів. Книги в Інтернеті не мають багато про що сказати, але все, що вона говорить :

Обчислити скалярний опис

У цьому описується одна з поведінок, на яку я нагадав раніше, відкладене виконання виразів. Я писав про деякі інші поточні поведінки (які можуть змінитись у будь-який час) у цій публікації блогу .

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

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

Підсумовуючи це, поточна поведінка не визначена, і в планах виконання немає нічого особливого, що допоможе вам зрозуміти, що сталося (і не завжди буде зручно приєднати відладчик, щоб вивчити детальну поведінку двигуна, як у публікації блогу).

Якщо у вас трапляються випадки, коли проблеми скалярної оцінки мають значення для продуктивності, підніміть проблему з підтримкою Microsoft. Це найкращий спосіб надати зворотній зв’язок для покращення майбутніх версій продукту.


3

Як зазначається в коментарі до вашого запитання, відповідь (на мій досвід, принаймні) "так". Як правило, SQL Server досить розумний, щоб уникнути повторних обчислень. Можливо, ви могли б це підтвердити, показавши план виконання в SQL Server Management Studio. Кожне обчислене поле позначається Exprxxxxx(де xxxxx - це число). Якщо ви знаєте, що шукати, ви повинні мати можливість перевірити, що він використовує той самий вираз.

Щоб додати до дискусії, ваш інший естетичний варіант - це загальний вираз таблиці :

with [cte] as
(
    select
        (case when a.type = 'workover' then 'workover' else 
        (case when substring(c.category, 2, 1) = 'd' then 'drilling'
              when substring(c.category, 2, 1) = 'c' then 'completion'
              when substring(c.category, 2, 1) = 'w' then 'workover'
              else 'other' end)
         end)) as [group_key],
         *
    from
        [some_table]
)
select
    [group_key],
    count(*) as [count]
from
    [cte]
group by
    [group_key]

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

Хоча це питання не стосується цього питання, вони можуть посилатися на себе і таким чином використовуватися для побудови рекурсивних запитів.


@Quick Джо Сміт: Я вважаю, що ви правильні щодо Exprxxxxx, оскільки я також це бачив. Однак якщо я даю ім'я виразу вручну (випадок ... кінець) як OpType, а потім використовую поле OpType у пункті GROUP BY, я отримую помилку, що це недійсне ім'я стовпця.
Доктор Дрю

На жаль, часто ваш єдиний вихід із вказівки виразу два рази - це використовувати один із перерахованих вище методів: CTE, перегляд або вкладений запит.
Швидкий Джо Сміт

2
Якщо ви також не знаєте про CROSS APPLY .
Андрій М

Використання cross applyв цьому випадку - трохи розтягнення, і це, швидше за все, завдасть шкоди продуктивності, запровадивши непотрібне самостійне з'єднання.
Швидкий Джо Сміт

2
Я не думаю, що ви "отримали" пропозицію. CROSS APPLYПросто визначає псевдонім з стовпців в одній і тій же рядку. Немає потреби в приєднанні. наприкладSELECT COUNT(*), hilo FROM master..spt_values CROSS APPLY (VALUES(high + low)) V(hilo) GROUP BY hilo
Мартін Сміт

1

Продуктивність - це лише один аспект. Інше - ремонтопридатність.

Особисто я схильний робити таке:

SELECT T.GroupingKey, SUM(T.value)
FROM
(
    SELECT 
        A.*
        (CASE WHEN A.type = 'Workover' THEN 'Workover' ELSE 
        (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' ELSE 'Other' END)
        END) AS GroupingKey
    FROM Table AS A
) AS T

GROUP BY T.GroupingKey

ОНОВЛЕННЯ:

Якщо ви не любите займатися вкладанням, ви можете створити VIEW для кожної таблиці, де потрібно використовувати складні вирази.

CREATE VIEW TableExtended
AS 
SELECT 
    A.*
    (CASE WHEN A.type = 'Workover' THEN 'Workover' ELSE 
    (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' ELSE 'Other' END)
    END) AS GroupingKey
FROM Table AS A

Тоді ви можете зробити вибір, не роблячи додаткові гніздування;

SELECT GroupingKey, SUM(value)
FROM TableExtended
GROUP BY GroupingKey
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.