PostgreSQL: использование вычисляемого столбца в том же запросе

У меня проблемы с использованием вычисляемого столбца в postgres. Аналогичный код, который работает в SQL, приведен ниже, возможно ли воссоздать его в PostgreSQL?

select cost_1, quantity_1, cost_2, quantity_2, 
      (cost_1 * quantity_1) as total_1,
      (cost_2 * quantity_2) as total_2,
      (calculated total_1 + calculated total_2) as total_3
from data;

В PostgreSQL аналогичный код возвращает ошибку, которая:

столбцы total_1 и total_2 не существуют.


person user1146150    schedule 12.01.2012    source источник
comment
возможный дубликат PostgreSQL Views: ссылка на одно вычисляемое поле в другом вычисляемое поле   -  person Andriy M    schedule 12.01.2012
comment
works in SQL? Вы показали нам SQL, а потом заявили, что он не работает. Итак, что вы имеете в виду под в SQL?   -  person a_horse_with_no_name    schedule 13.01.2012


Ответы (5)


Вам нужно обернуть оператор SELECT в производную таблицу, чтобы иметь доступ к псевдониму столбца:

select cost1,
       quantity_1,
       cost_2,
       quantity_2
       total_1 + total_2 as total_3
from (
    select cost_1, 
           quantity_1, 
           cost_2, 
           quantity_2, 
           (cost_1 * quantity_1) as total_1,
           (cost_2 * quantity_2) as total_2
    from data
) t

Никаких штрафов за производительность при этом не будет.

действительно удивлен тем, что ваш исходный оператор SQL вообще работает в СУБД)

person a_horse_with_no_name    schedule 12.01.2012
comment
Исходный пост выглядит как SAS PROC SQL, который позволяет это - person Andrew; 18.06.2015
comment
Я хотел бы иметь здесь определение let ... = .... как в Haskell, чтобы определить локальное вычисление вместо подзапроса. Это было бы прекрасно. Тем не менее, спасибо за хороший ответ! - person Hartmut P.; 11.03.2016
comment
В чем преимущество/недостаток обертывания оператора select, а не использования CTE, например? Спасибо :) - person Davita; 20.05.2016
comment
@Davita: в Postgres CTE оптимизирована иначе, чем производная таблица. В большинстве случаев (включая этот) это не имеет значения, поэтому все сводится к личным предпочтениям. Кроме того, не все привыкли к CTE, и это привело бы к еще одному уровню сложности ответа, которого я хотел избежать. - person a_horse_with_no_name; 20.05.2016

Если вам не нравится обертывать весь запрос внешним запросом, вы можете использовать LATERAL для вычисления промежуточных значений total_1 и total_2:

SELECT cost_1, quantity_1, cost_2, quantity_2, total_1, total_2,
       total_1 + total_2 AS total_3
FROM data
,LATERAL(SELECT cost_1 * quantity_1, cost_2 * quantity_2) AS s1(total_1,total_2);

демонстрация DBFiddle

Выход:

╔═════════╦═════════════╦═════════╦═════════════╦══════════╦══════════╦═════════╗
║ cost_1  ║ quantity_1  ║ cost_2  ║ quantity_2  ║ total_1  ║ total_2  ║ total_3 ║
╠═════════╬═════════════╬═════════╬═════════════╬══════════╬══════════╬═════════╣
║      1  ║          2  ║      3  ║          4  ║       2  ║      12  ║      14 ║
║      3  ║          5  ║      7  ║          9  ║      15  ║      63  ║      78 ║
║     10  ║          5  ║     20  ║          2  ║      50  ║      40  ║      90 ║
╚═════════╩═════════════╩═════════╩═════════════╩══════════╩══════════╩═════════╝
person Lukasz Szozda    schedule 10.04.2016
comment
О, классно. ТИЛ. Это гораздо удобнее для некоторых случаев, включая мой, который создает представление, которое делает много уровней ссылок на предыдущие вычисления. Встраивание повторных вычислений подвержено ошибкам, а вложенные подзапросы имеют для меня около 9 уровней вложенности. Версию LATERAL намного проще отслеживать и поддерживать. - person Bo Jeanes; 16.03.2017
comment
Что такое s1 в боковой части здесь? Методом проб и ошибок я обнаружил, что это может быть произвольная строка - зачем тогда это нужно? - person Robin Nemeth; 17.06.2019
comment
@RobinNemeth s1 — это псевдоним производной таблицы. Вы можете использовать его в SELECT например SELECT s1.total_1 + s1.total_2 AS total_3 - person Lukasz Szozda; 17.06.2019

Как правило, вам нужно знать две вещи о предложении SELECT:

  • Хотя он пишется первым, он оценивается последним, за исключением предложения ORDER BY. Вот почему вы не можете использовать какие-либо вычисляемые поля или псевдонимы в любом другом предложении (в частности, в предложении WHERE) кроме в предложении ORDER BY.
  • Вычисления в предложении SELECT выполняются параллельно или, по крайней мере, обрабатываются так, как будто они и есть. Вот почему вы не можете использовать один расчет как часть другого.

Итак, короткий ответ заключается в том, что вы не можете, и это задумано.

Заметным исключением является Microsoft Access, где вы действительно можете использовать вычисления в последующих столбцах и предложениях WHERE. Однако, хотя это удобно, на самом деле это не преимущество: несоблюдение вышеуказанных принципов менее эффективно. Но это нормально для легких баз данных, для которых предполагается использовать Access.

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

Изменить

Вот пример того, почему использование вычисляемых столбцов может вызвать путаницу. В Австралии мы измеряем рост в сантиметрах, но до сих пор в некоторых местах используются древние дюймы (1 дюйм = 2,54 см).

SELECT
    id,
    height/2.54 as height, -- cm -> in
    case when height>175 then 'tall' else '' end as comment
FROM people;

Здесь CASE по-прежнему использует исходное значение height.

person Manngo    schedule 10.03.2017
comment
Кроме того, всякий раз, когда я имею дело с полями с физическими единицами, я всегда включаю единицу, где это возможно, в схему, но если я создаю сложные запросы, я бы, например, использовал псевдоним height как height_cm. Это менее многословно, но всегда полезно, когда вам может понадобиться выполнить преобразование между метрическими и имперскими системами. - person Will Ediger; 08.01.2020

Вы пытаетесь использовать псевдонимы столбцов в выражении. Если система позволяет вам это делать, это просто синтаксический сахар. Это должно работать на любом диалекте SQL.

select 
 cost_1
,quantity_1
,cost_2
,quantity_2
,cost_1 * quantity_1 as total_1
,cost_2 * quantity_2 as total_2
,(cost_1 * quantity_1) + (cost_2 * quantity_2) as total_3 

from data;
person tponthieux    schedule 12.01.2012
comment
Не в случае postgres! - person Suyash Gulati; 07.12.2018

person    schedule
comment
Не в случае postgres! - person Suyash Gulati; 07.12.2018