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

在WHERE子句中無法使用在選定列的列表中設置爲變量的計算值'BalanceDue'。WHERE子句中的引用別名(在SELECT中計算)

有沒有辦法呢?在這個相關的問題(Using a variable in MySQL Select Statment in a Where Clause)中,似乎答案實際上不是,您只需將計算結果(在查詢中執行該計算)寫出兩次,其中沒有一項是令人滿意的。

回答

168

除了在ORDER BY中,您不能引用別名,因爲SELECT是評估的第二個最後一個子句。兩種解決方法:

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

還是重複剛纔的表情:

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

我更喜歡後者。如果表達式非常複雜(或者計算成本很高),那麼應該考慮一個計算列(也可能是持久化),特別是如果很多查詢引用同一個表達式。

PS你的恐懼似乎毫無根據。至少在這個簡單的例子中,即使你已經引用了兩次,SQL Server也足夠聰明,只能執行一次計算。繼續比較計劃;你會看到它們是相同的。如果您有一個更復雜的情況,您可以看到多次評估表達式,請發佈更復雜的查詢和計劃。

這裏有5個示例查詢,所有產生完全相同的執行計劃:爲所有五個查詢

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; 

生成的計劃:

enter image description here

+0

非常感謝亞倫! –

+7

哇。 SQL Server足夠聰明,只能執行一次計算 – alternatefaraz

+3

哇這是一個非常高質量的答案! – Siddhartha

0

你可以做到這一點使用cross join

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