【问题标题】:Reference alias (calculated in SELECT) in WHERE clauseWHERE 子句中的引用别名(在 SELECT 中计算)
【发布时间】:2012-06-25 00:41:52
【问题描述】:
SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
WHERE BalanceDue > 0 --error

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

有什么办法可以吗?在这个相关问题 (Using a variable in MySQL Select Statment in a Where Clause) 中,答案似乎是,实际上,不,您只需写出计算(在查询中执行该计算)两次,其中没有一个很满意。

【问题讨论】:

    标签: sql sql-server tsql


    【解决方案1】:

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

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

    【讨论】:

    • 哇。 SQL Server 足够聪明,只执行一次计算
    • 哇,这是一个非常高质量的答案!
    • 我需要在 MERGE 语句中添加一些额外的条件,这是我让它工作的唯一方法。谢谢!
    • @EricBurdo 如果您使用的是MERGE,请确保您已将所有这些考虑在内:MERGE with caution
    • @AaronBertrand - 您的第一个查询是子查询还是派生表?即使在快速谷歌搜索之后,我也不知道两者之间的区别。
    【解决方案2】:

    你可以使用cross apply来做到这一点

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

    【讨论】:

      【解决方案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
      

      Syed Mehroz Alam致敬。

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          【解决方案5】:

          一个简单的方法

          SELECT *  FROM Invoices GROUP BY id
          HAVING(InvoiceTotal - PaymentTotal - CreditTotal) > 0
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-01-12
            • 2019-01-23
            • 1970-01-01
            • 2018-10-01
            • 1970-01-01
            相关资源
            最近更新 更多