2012-06-25 8 views
88
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.Alias ​​odniesienia (obliczany w SELECT) w klauzuli WHERE

Czy istnieje sposób, aby to zrobić? W tym pokrewnym pytaniu (Using a variable in MySQL Select Statment in a Where Clause) wygląda na to, że odpowiedź brzmiałaby tak, nie, po prostu napisałbyś obliczenia (i wykonać to obliczenie w zapytaniu) dwa razy, z których żadna nie jest zadowalająca.

Odpowiedz

168

Nie można odwoływać się do aliasu za wyjątkiem ORDER BY, ponieważ SELECT jest drugą ostatnią klauzulą, która została oceniona. Dwa obejścia:

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

Albo tylko powtórzyć wyrażenie:

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

wolę to drugie. Jeśli wyrażenie jest bardzo skomplikowane (lub kosztowne do obliczenia), powinieneś raczej rozważyć kolumnę obliczeniową (i być może utrwaloną) zamiast tego, szczególnie 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ływał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 jest wielokrotnie sprawdzane, opublikuj bardziej złożone zapytanie i plany.

Oto 5 przykład odpytuje że wszystko uzyskując 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; 

wynikających plan dla wszystkich pięciu pytań:

enter image description here

+0

Wielkie dzięki Aaron! –

+7

Wow. Serwer SQL jest wystarczająco inteligentny, aby wykonać obliczenia tylko raz. – alternatefaraz

+3

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

0

Można to zrobić za pomocą cross join

SELECT c.BalanceDue AS BalanceDue 
FROM Invoices 
cross join (select (InvoiceTotal - PaymentTotal - CreditTotal) as BalanceDue) as c 
WHERE c.BalanceDue > 0; 
Powiązane problemy