Alias ​​odniesienia (obliczany w SELECT) w klauzuli WHERE


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

Obliczona wartość BalanceDueustawiona jako zmienna na liście wybranych kolumn nie może być używana w WHEREklauzuli.

Czy jest na to sposób? W tym pokrewnym pytaniu ( przy użyciu zmiennej w instrukcji MySQL Select w klauzuli Where ) wydaje się, że odpowiedź brzmiałaby w rzeczywistości nie, po prostu wypiszesz obliczenie ( i wykonasz je w zapytaniu) dwa razy, żadna z co jest zadowalające.

Odpowiedzi:


248

Nie możesz odwoływać się do aliasu, z wyjątkiem ORDER BY, ponieważ SELECT jest przedostatnią 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), prawdopodobnie powinieneś rozważyć zamiast tego kolumnę obliczaną (i być może utrwaloną), zwłaszcza jeśli wiele zapytań odnosi się do tego samego wyrażenia.

PS Twoje obawy wydają się bezpodstawne. Przynajmniej w tym prostym przykładzie SQL Server jest na tyle inteligentny, że wykonuje obliczenia tylko raz, nawet jeśli odwołałeś się do niego dwukrotnie. Śmiało i porównaj plany; zobaczysz, że są identyczne. Jeśli masz bardziej złożony przypadek, w którym wyrażenie zostało ocenione wielokrotnie, 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ń:

wprowadź opis obrazu tutaj


13
Łał. SQL Server jest na tyle sprytny, że wykonuje obliczenia tylko raz
— alternatefaraz

7
Wow, to bardzo wysokiej jakości odpowiedź!
— Siddhartha

Potrzebowałem dodatkowych warunków w oświadczeniu MERGE i był to jedyny sposób, aby to zadziałało. Dzięki!
— Eric Burdo,

1
@EricBurdo Jeśli używasz MERGE, upewnij się, że wziąłeś to wszystko pod uwagę: MERGEz ostrożnością .
— Aaron Bertrand,

@AaronBertrand - Czy pierwsze zapytanie jest podzapytaniem czy tabelą pochodną? Nie znam różnicy między nimi, nawet po szybkim googlowaniu.
— MasterJoe

11

Możesz to zrobić za pomocą cross apply

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

4

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

Sprzężenie krzyżowe niekoniecznie pozwala na odpowiednie powiązanie z kolumnami tabeli, do których się odwołuje, jednak OUTER APPLY tak - i traktuje wartości null w bardziej przejrzysty sposób.

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

Kudos dla Syed Mehroz Alam .

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.