Почему я не могу использовать псевдонимы столбцов в следующем выражении SELECT?

Могу ли я изменить следующий, чтобы использовать псевдонимы столбца avg_time а также cnt в выражении ROUND(avg_time * cnt, 2)?

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;

4 ответа

Решение

Вы можете использовать ранее созданный псевдоним в GROUP BY или же HAVING но не в SELECT или же WHERE это потому, что инструкция обрабатывает все части 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;

Порядок выполнения запроса НЕ совпадает с тем, как он написан. "Общая" позиция заключается в том, что предложения оцениваются в следующей последовательности:

FROM
WHERE
GROUP BY
HAVING
SELECT
ORDER BY

Следовательно, псевдонимы столбцов неизвестны большинству запросов до тех пор, пока предложение select не будет завершено (и поэтому вы можете использовать псевдонимы в предложении ORDER BY). Однако псевдонимы таблиц, которые установлены в предложении from, понимаются в разделах, где можно упорядочить.

Наиболее распространенным обходным решением является инкапсуляция вашего запроса в "производную таблицу".

Рекомендуемое прочтение: порядок выполнения SQL-запроса

Примечание: разные базы данных SQL имеют разные конкретные правила относительно использования алисов

Псевдонимы недоступны до тех пор, пока виртуальное отношение фактически не будет создано. Если вы хотите создать дополнительные выражения, используя сами псевдонимы, вам нужно будет создать виртуальное отношение, используя в качестве подзапроса, а не выполнять дополнительный запрос поверх него. Поэтому я бы изменил ваш запрос следующим образом:

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

Я хочу добавить здесь причину, по которой ваш второй запрос работал, потому что планировщик запросов распознал эти столбцы, как определено непосредственно в таблице, из которой вы их запрашиваете.

Либо повторить выражения:

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;
Другие вопросы по тегам