为什么不能在下一个 SELECT 表达式中使用列别名?

Why can't I use column aliases in the next SELECT expression?

我能否修改下一个以在表达式 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;

您可以在 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;

查询的执行顺序(以及表达式和别名的 求值 )与其书写方式不同。 "general" 位置是按以下顺序评估子句:

FROM
WHERE
GROUP BY
HAVING
SELECT
ORDER BY

因此,列别名对于大多数查询来说是未知的直到select子句完成(这是为什么你 可以 在 ORDER BY 子句中使用别名)。然而,在 from 子句中建立的 table 别名 在 where to order by 子句中被理解。

最常见的解决方法是将您的查询封装到 "derived table"

建议阅读:Order Of Execution of the SQL query

注意:不同的 SQL dbms 在使用别名方面有不同的特定规则

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

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

要么重复表达式:

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;

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

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;
)

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