Alias referencyjny (obliczony w SELECT) w klauzuli WHERE

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

Obliczona wartość 'BalanceDue' ustawiona jako zmienna na liście wybranych kolumn nie może być użyta w klauzuli WHERE.

Jest jakiś sposób, że może? W tym powiązanym pytaniu (używając zmiennej w MySQL Select Statment w klauzuli Where), wydaje się, że odpowiedź brzmiałaby, w rzeczywistości, nie, po prostu zapisałbyś obliczenia (i wykonaj te obliczenia w zapytaniu) dwa razy, z których żadna nie jest zadowalająca.

Author: Community, 2012-06-25

3 answers

Nie można odwoływać się do aliasu z wyjątkiem kolejności, ponieważ SELECT jest drugą ostatnią klauzulą, która jest oceniana. Dwa obejścia:

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

Lub po prostu powtórz wyrażenie:

SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
WHERE  (InvoiceTotal - PaymentTotal - CreditTotal)  > 0;
Wolę to drugie. Jeśli wyrażenie jest niezwykle złożone (lub kosztowne w obliczeniu), powinieneś prawdopodobnie rozważyć kolumnę obliczeniową (i być może nadal), zwłaszcza jeśli wiele zapytań odnosi się do tego samego wyrażenia. PS twoje obawy wydają się bezpodstawne. W tym prostym przykładzie przynajmniej, SQL Server jest wystarczająco inteligentny, aby wykonać obliczenia tylko raz, nawet jeśli odwołałeś się do nich dwa razy. Śmiało, porównaj plany, zobaczysz, że są identyczne. Jeśli masz bardziej złożony przypadek, w którym wyrażenie jest wielokrotnie oceniane, Opublikuj bardziej złożone zapytanie i plany.

Oto 5 przykładowych zapytań, które dają dokładnie ten sam plan wykonania:

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;

Wynikowy plan dla wszystkich pięciu zapytań:

Tutaj wpisz opis obrazka

 194
Author: Aaron Bertrand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-25 01:51:27

Możesz to zrobić używając cross apply

SELECT c.BalanceDue AS BalanceDue
FROM Invoices
cross apply (select (InvoiceTotal - PaymentTotal - CreditTotal) as BalanceDue) as c
WHERE  c.BalanceDue  > 0;
 3
Author: Manoj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-27 09:20:26

W rzeczywistości możliwe jest skuteczne zdefiniowanie zmiennej, która może być używana zarówno w klauzulach SELECT, WHERE, jak i innych.

Połączenie krzyżowe nie musi umożliwiać odpowiedniego powiązania z kolumnami tabeli, do której odnoszą się odniesienia, jednak zewnętrzne APPLY to robi - i traktuje null bardziej przejrzyście.

SELECT
    vars.BalanceDue
FROM
    Entity e
OUTER APPLY (
    SELECT
        -- variables   
        BalanceDue = e.EntityTypeId,
        Variable2 = ...some..long..complex..expression..etc...
    ) vars
WHERE
    vars.BalanceDue > 0
/ Align = "center" bgcolor = "# e0ffe0 " / cesarz chin / / align = center /
 2
Author: Peter Aylett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-17 13:15:32