2012-06-25 3 views
88

Der berechnete Wert 'BalanceDue', der in der Liste der ausgewählten Spalten als Variable festgelegt ist, kann in der WHERE-Klausel nicht verwendet werden.Referenzalias (berechnet in SELECT) in der WHERE-Klausel

Gibt es einen Weg, wie es geht? In dieser verwandten Frage (Using a variable in MySQL Select Statment in a Where Clause), scheint es, als wäre die Antwort, tatsächlich, nein, würden Sie einfach die Berechnung (und führen Sie diese Berechnung in der Abfrage) zweimal, von denen keine zufriedenstellend ist.

Antwort

168

Sie können auf einen Alias ​​nur in ORDER BY verweisen, da SELECT die zweitletzte Klausel ist, die ausgewertet wird. Zwei Abhilfen:

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

Oder einfach wiederholen Sie den Ausdruck:

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

Ich ziehe das letztere. Wenn der Ausdruck extrem komplex (oder kostspielig in der Berechnung) ist, sollten Sie stattdessen eine berechnete Spalte (und möglicherweise beibehalten) in Betracht ziehen, insbesondere dann, wenn sich viele Abfragen auf denselben Ausdruck beziehen.

PS Ihre Ängste scheinen unbegründet. In diesem einfachen Beispiel ist SQL Server intelligent genug, um die Berechnung nur einmal durchzuführen, obwohl Sie sie zweimal referenziert haben. Gehen Sie voran und vergleichen Sie die Pläne; Sie werden sehen, dass sie identisch sind. Wenn Sie einen komplexeren Fall haben, in dem der Ausdruck mehrfach ausgewertet wird, buchen Sie bitte die komplexere Abfrage und die Pläne.

Hier sind 5 Beispiel fragt, dass alle exakt die gleichen Ausführungsplan ergeben:

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; 

Resultierende Plan für alle fünf Anfragen:

enter image description here

+0

vielen Dank Aaron! –

+7

Wow. SQL Server ist schlau genug, um die Berechnung nur einmal durchzuführen – alternatefaraz

+3

Wow, das ist eine sehr hochwertige Antwort! – Siddhartha

0

du cross join

mit tun können
SELECT c.BalanceDue AS BalanceDue 
FROM Invoices 
cross join (select (InvoiceTotal - PaymentTotal - CreditTotal) as BalanceDue) as c 
WHERE c.BalanceDue > 0; 
Verwandte Themen