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

El valor calculado 'BalanceDue' que se establece como una variable en la lista de columnas seleccionadas no se puede utilizar en la cláusula WHERE.Alias ​​de referencia (calculado en SELECCIONAR) en la cláusula WHERE

¿Hay alguna manera de hacerlo? En esta pregunta relacionada (Using a variable in MySQL Select Statment in a Where Clause), parece que la respuesta sería, en realidad, no, simplemente escribiría el cálculo (y realizando ese cálculo en la consulta) dos veces, ninguna de las cuales es satisfactoria.

Respuesta

168

No puede hacer referencia a un alias excepto en ORDER BY porque SELECT es la segunda última cláusula que se evalúa. Dos soluciones:

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

O sólo tiene que repetir la expresión:

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

prefiero este último. Si la expresión es extremadamente compleja (o costosa de calcular), probablemente deba considerar una columna calculada (y tal vez persistente), especialmente si muchas consultas se refieren a esta misma expresión.

PS sus temores parecen infundados. En este sencillo ejemplo al menos, SQL Server es lo suficientemente inteligente como para realizar el cálculo una sola vez, aunque lo hayas hecho dos veces. Adelante y compara los planes; verás que son idénticos. Si tiene un caso más complejo en el que vea la expresión evaluada varias veces, publique la consulta más compleja y los planes.

Aquí hay 5 consultas de ejemplo que todos conseguirán exactamente el mismo plan de ejecución:

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; 

plan resultante para los cinco consultas:

enter image description here

+0

muchas gracias Aaron! –

+7

Wow. SQL Server es lo suficientemente inteligente como para realizar solo el cálculo una vez – alternatefaraz

+3

¡Vaya, esta es una respuesta de muy alta calidad! – Siddhartha

0

Usted puede hacer esto utilizando cross join

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