Довідковий псевдонім (обчислюється SELECT) у пункті WHERE


130
SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
WHERE BalanceDue > 0 --error

Обчислене значення "BalanceDue", яке встановлено у вигляді змінної у списку вибраних стовпців, не може бути використане у пункті WHERE.

Чи є спосіб, який він може? У цьому пов'язаному питанні ( Використання змінної у MySQL Select Statment у пункті де ), схоже, що відповідь була б насправді ні, ви просто випишете обчислення ( і виконаєте цей розрахунок у запиті) двічі, жоден з що задовільно.

Відповіді:


237

Ви не можете посилатися на псевдонім, за винятком порядку ORDER BY, оскільки SELECT є другим останнім оцінюваним пунктом. Два обхідні шляхи:

SELECT BalanceDue FROM (
  SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
  FROM Invoices
) AS x
WHERE BalanceDue > 0;

Або просто повторіть вираз:

SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
WHERE  (InvoiceTotal - PaymentTotal - CreditTotal)  > 0;

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

PS Ваші побоювання здаються безпідставними. Принаймні, у цьому простому прикладі SQL Server досить розумний, щоб виконати обчислення лише один раз, навіть якщо ви посилалися на нього двічі. Йти вперед і порівняти плани; ви побачите, що вони однакові. Якщо у вас є складніший випадок, коли ви бачите вираз, оцінений декілька разів, будь ласка, опублікуйте складніший запит та плани.

Ось 5 прикладних запитів, які дають такий самий план виконання:

SELECT LEN(name) + column_id AS x
FROM sys.all_columns
WHERE LEN(name) + column_id > 30;

SELECT x FROM (
SELECT LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE x > 30;

SELECT LEN(name) + column_id AS x
FROM sys.all_columns
WHERE column_id + LEN(name) > 30;

SELECT name, column_id, x FROM (
SELECT name, column_id, LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE x > 30;

SELECT name, column_id, x FROM (
SELECT name, column_id, LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE LEN(name) + column_id > 30;

Результатний план для всіх п'яти запитів:

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


11
Ого. SQL Server досить розумний, щоб виконувати обчислення лише один раз
alternatefaraz

5
Вау, це дуже якісна відповідь!
Сіддхартха

Мені потрібні були додаткові умови в заяві MERGE, і це був єдиний спосіб, коли я міг змусити його працювати. Дякую!
Ерік Бурдо

1
@EricBurdo Якщо ви користуєтесь MERGE, переконайтеся, що ви все це врахували: MERGEз обережністю .
Аарон Бертран

11

Це можна зробити за допомогою cross apply

SELECT c.BalanceDue AS BalanceDue
FROM Invoices
cross apply (select (InvoiceTotal - PaymentTotal - CreditTotal) as BalanceDue) as c
WHERE  c.BalanceDue  > 0;

4

Насправді можливо ефективно визначити змінну, яка може бути використана як у пунктах SELECT, WHERE та інших.

Перехресне з'єднання не обов'язково дозволяє відповідне прив'язування до стовпців посилаються на таблицю, однак OUTER APPLY робить - і обробляє нулі більш прозоро.

SELECT
    vars.BalanceDue
FROM
    Entity e
OUTER APPLY (
    SELECT
        -- variables   
        BalanceDue = e.EntityTypeId,
        Variable2 = ...some..long..complex..expression..etc...
    ) vars
WHERE
    vars.BalanceDue > 0

Престижність Сайед Mehroz Алам .

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