【问题标题】:Why can't I use column aliases in the next SELECT expression?为什么我不能在下一个 SELECT 表达式中使用列别名?
【发布时间】:2016-01-22 21:03:20
【问题描述】:

我可以修改下一个以在表达式ROUND(avg_time * cnt, 2) 中使用列别名avg_timecnt 吗?

SELECT 
    COALESCE(ROUND(stddev_samp(time), 2), 0) as stddev_time, 
    MAX(time) as max_time, 
    ROUND(AVG(time), 2) as avg_time, 
    MIN(time) as min_time, 
    COUNT(path) as cnt, 
    ROUND(avg_time * cnt, 2) as slowdown, path
FROM 
    loadtime
GROUP BY
    path
ORDER BY
    avg_time DESC
LIMIT 10;

引发下一个错误:

ERROR:  column "avg_time" does not exist
LINE 7:  ROUND(avg_time * cnt, 2) as slowdown, path

不过,下一个可以正常工作(使用主表达式而不是列别名:

SELECT 
    COALESCE(ROUND(stddev_samp(time), 2), 0) as stddev_time, 
    MAX(time) as max_time, 
    ROUND(AVG(time), 2) as avg_time, 
    MIN(time) as min_time, 
    COUNT(path) as cnt, 
    ROUND(AVG(time) * COUNT(path), 2) as slowdown, path
FROM 
    loadtime
GROUP BY
    path
ORDER BY
    avg_time DESC
LIMIT 10;

【问题讨论】:

    标签: sql postgresql column-alias


    【解决方案1】:

    您可以在GROUP BYHAVING 语句中使用先前创建的别名,但不能在SELECTWHERE 语句中使用。这是因为程序同时处理了所有的SELECT 语句,还不知道别名的值。

    解决方案是将查询封装在子查询中,然后别名在外面可用。

    SELECT stddev_time, max_time, avg_time, min_time, cnt, 
           ROUND(avg_time * cnt, 2) as slowdown
    FROM (
            SELECT 
                COALESCE(ROUND(stddev_samp(time), 2), 0) as stddev_time, 
                MAX(time) as max_time, 
                ROUND(AVG(time), 2) as avg_time, 
                MIN(time) as min_time, 
                COUNT(path) as cnt, 
                path
            FROM 
                loadtime
            GROUP BY
                path
            ORDER BY
                avg_time DESC
            LIMIT 10
       ) X;
    

    【讨论】:

    • 谢谢。我已经尝试过您的示例查询,但很抱歉,同样的错误。
    【解决方案2】:

    查询的执行顺序(以及表达式和别名的求值)与它的编写方式不同。 “一般”位置是子句按以下顺序评估:

    FROM
    WHERE
    GROUP BY
    HAVING
    SELECT
    ORDER BY
    

    因此,列别名对于大多数查询来说是未知的直到选择子句完成(这就是您可以使用别名的原因在 ORDER BY 子句中)。但是,在 from 子句中建立的 表别名 在 where to order by 子句中可以理解。

    最常见的解决方法是将查询封装到“派生表”中

    推荐阅读:Order Of Execution of the SQL query

    注意:不同的 SQL dbms 对于别名的使用有不同的具体规则

    编辑 提醒读者注意逻辑子句序列的目的是通常(但不总是)别名仅在声明别名的子句之后才变得可引用。其中最常见的是SELECT 子句中声明的别名可以被ORDER BY 子句使用。特别是,在 SELECT 子句中声明的别名不能在同一 SELECT 子句中引用。

    但请注意,由于产品的差异,并非每个 dbms 都会以这种方式运行

    【讨论】:

    • 你从哪里得到这个序列?我认为GROUP BYHAVING 追求SELECT
    • 这是SQL查询的“逻辑运算”。当你购买钉子来建造东西时,你要做的第一件事就是去商店然后选择钉子,即FROM the_shop SELECT the_nails。它是在 SQL 中,您可以访问表并加入它们(FROM),选择哪些行(WHERE),如果需要,可以从这些组中选择(HAVING),然后现在我们有了正确的数据(SELECT),我们可以对其进行排序。
    • 在 SQL Server 中仔细检查后,Oracle CANTGROUP BY Postgres CAN 中使用别名,而 MySQL 变得流氓和 CANSELECT 中使用别名
    • 正如我所说,它在 dbms 之间确实有所不同,例如我知道 terradata 确实允许在 order by 之前使用列别名;但是,通用方法是假设您不能。例如,您可以在 mssql 中使用 apply 运算符来获取可在整个查询中使用的列别名 - 但这根本不是通用的。
    • 这并没有解释为什么别名在 SELECT 语句本身中无法识别,因为 OP 正在询问
    【解决方案3】:

    在实际创建虚拟关系之前,别名不可用,如果您想使用别名本身执行其他表达式,则必须使用 as 子查询创建虚拟关系,而不是在其之上运行附加查询。因此,我会将您的查询修改为以下内容:

    SELECT stddev_time, max_time, avg_time, min_time, ROUND(avg_time * cnt, 2) as slowdown, path FROM
    (
    SELECT 
        COALESCE(ROUND(stddev_samp(time), 2), 0) as stddev_time, 
        MAX(time) as max_time, 
        ROUND(AVG(time), 2) as avg_time, 
        MIN(time) as min_time, 
        COUNT(path) as cnt, 
        ROUND(AVG(time) * COUNT(path), 2) as slowdown, path
    FROM 
        loadtime
    GROUP BY
        path
    ORDER BY
        avg_time DESC
    LIMIT 10;
    )
    

    我想在此处添加您的第二个查询有效的原因是因为查询规划器将这些列识别为直接在您查询它们的表中定义的。

    【讨论】:

      【解决方案4】:

      要么重复表达式:

      ROUND(ROUND(AVG(time), 2) * COUNT(path), 2) as slowdown
      

      或使用子查询:

      SELECT *, ROUND(avg_time * cnt, 2) as slowdown FROM (
        SELECT 
          COALESCE(ROUND(stddev_samp(time), 2), 0) as stddev_time, 
          MAX(time) as max_time, 
          ROUND(AVG(time), 2) as avg_time, 
          MIN(time) as min_time, 
          COUNT(path) as cnt, 
          path
        FROM loadtime
        GROUP BY path) x
      ORDER BY avg_time DESC
      LIMIT 10;
      

      【讨论】:

      • 如果我重复 ROUND 和 AVG 聚合,postgresql 解释器会再做一次相同的计算吗?
      • 不确定,db 有一些优化器来解决这些问题。您必须检查查询计划才能确定。
      • @sergz 这取决于优化器,但大多数 rdbms 只会执行一次操作。显然,表达式必须是行列式的。
      猜你喜欢
      • 2018-06-19
      • 1970-01-01
      • 2022-07-11
      • 1970-01-01
      • 2021-06-17
      • 2013-06-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多