SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
WHERE BalanceDue > 0 --error
在WHERE子句中不能使用在所选列列表中设置为变量的计算值“BalanceDue”。
有没有办法可以?在这个相关的问题(Using a variable in MySQL Select Statment in a Where Clause)中,似乎答案是,实际上,不,你只会写出计算(和在查询中执行该计算)两次,没有其中令人满意。
答案 0 :(得分:211)
除了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;
所有五个查询的结果计划:
答案 1 :(得分:6)
您可以使用cross apply
SELECT c.BalanceDue AS BalanceDue
FROM Invoices
cross apply (select (InvoiceTotal - PaymentTotal - CreditTotal) as BalanceDue) as c
WHERE c.BalanceDue > 0;
答案 2 :(得分:3)
实际上可以有效地定义一个可以在SELECT,WHERE和其他子句中使用的变量。
交叉连接并不一定允许对引用的表列进行适当的绑定,但是OUTER APPLY可以 - 并且更透明地处理空值。
SELECT
vars.BalanceDue
FROM
Entity e
OUTER APPLY (
SELECT
-- variables
BalanceDue = e.EntityTypeId,
Variable2 = ...some..long..complex..expression..etc...
) vars
WHERE
vars.BalanceDue > 0
答案 3 :(得分:0)
作为强制在WHERE子句之前执行SELECT子句的评估的解决方法,您可以将前者放在子查询中,而后者保留 在主要查询中:
SELECT * FROM (
SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices) AS temp
WHERE BalanceDue > 0