2012-06-25 24 views
88
SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue 
FROM Invoices 
WHERE BalanceDue > 0 --error 

Il valore calcolato "BalanceDue" impostato come variabile nell'elenco di colonne selezionate non può essere utilizzato nella clausola WHERE.Alias ​​di riferimento (calcolato in SELECT) nella clausola WHERE

C'è un modo che può? In questa domanda correlata (Using a variable in MySQL Select Statment in a Where Clause), sembra che la risposta sarebbe, in realtà, no, si dovrebbe semplicemente scrivere il calcolo (e eseguire tale calcolo nella query) due volte, nessuna delle quali è soddisfacente.

risposta

168

Non è possibile fare riferimento a un alias tranne in ORDER BY perché SELECT è la penultima etichetta valutata. Due soluzioni:

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

Oppure è sufficiente ripetere l'espressione:

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

preferisco la seconda. Se l'espressione è estremamente complessa (o costosa da calcolare), dovresti probabilmente considerare una colonna calcolata (e forse persistente), soprattutto se molte query si riferiscono a questa stessa espressione.

PS le tue paure sembrano infondate. In questo semplice esempio, SQL Server è abbastanza intelligente da eseguire il calcolo solo una volta, anche se l'hai fatto riferimento due volte. Vai avanti e confronta i piani; vedrai che sono identici. Se hai un caso più complesso in cui vedi l'espressione valutata più volte, pubblica la query più complessa e i piani.

Qui ci sono 5 esempio query che tutto cedere lo stesso identico piano di esecuzione:

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; 

Con conseguente piano per tutte e cinque le domande:

enter image description here

+0

grazie mille Aaron! –

+7

Wow. SQL Server è abbastanza intelligente da eseguire il calcolo solo una volta – alternatefaraz

+3

Wow questa è una risposta di altissima qualità! – Siddhartha

0

È possibile farlo usando cross join

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