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

在选定列的列表中设置为变量的计算值BalanceDue不能在WHERE子句中使用。

有没有办法可以?在这个相关问题(在 Where 子句中使用 MySQL Select Statment 中的变量)中,答案似乎是,实际上,不,您只需写出计算(在查询中执行该计算)两次,没有这是令人满意的。

4

4 回答 4

270

您不能在 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;

所有五个查询的结果计划:

在此处输入图像描述

于 2012-06-25T00:54:29.927 回答
13

您可以使用cross apply

SELECT c.BalanceDue AS BalanceDue
FROM Invoices
cross apply (select (InvoiceTotal - PaymentTotal - CreditTotal) as BalanceDue) as c
WHERE  c.BalanceDue  > 0;
于 2017-03-29T07:04:37.697 回答
4

实际上可以有效地定义一个可以在 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

Syed Mehroz Alam 致敬

于 2018-04-17T13:15:32.517 回答
0

作为在 WHERE 子句之前强制评估 SELECT 子句的一种解决方法,您可以将前者放在子查询中,而后者保留在主查询中:

SELECT * FROM (
  SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
  FROM Invoices) AS temp
WHERE BalanceDue > 0
  
于 2020-11-13T00:34:32.823 回答