ОШИБКА: столбец указан более одного раза в определении VIEW

Это дополнительный вопрос к более раннему. У меня есть хранимая функция f1, которая принимает два аргумента и возвращает таблицу с 5 столбцами; на данный момент возвращаемые значения являются постоянными, позже они будут вычислены из аргументов.

У меня также есть таблица t1 с двумя столбцами, тип которых соответствует аргументам f1.

Теперь я хотел бы определить представление v1, содержащее объединение всех строк, возвращенных из f1, для всех пар аргументов, хранящихся в t1. Для данных примеров значений результат должен быть:

+---+---+---+---+---+---+---+
| 2 | 3 | a | b | 1 | c | d |
+---+---+---+---+---+---+---+
| 4 | 5 | a | b | 1 | c | d |
+---+---+---+---+---+---+---+

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

Я пробовал следующий оператор, но он дает мне это сообщение об ошибке:

ERROR: column "c4" specified more than once
CREATE VIEW v1 (c1, c2, c3, c4, c5)
AS SELECT * FROM
  (SELECT c1, c2 FROM t1) AS x,
  f1 (x.c1, x.c2);

Что я делаю не так?

Вот предыдущие операторы для настройки примера:

CREATE OR REPLACE FUNCTION f1 (a1 INTEGER, a2 INTEGER)
RETURNS TABLE (c1 VARCHAR(20), c2 VARCHAR(20), c3 INTEGER, c4 VARCHAR(20), c5 VARCHAR(128))
AS $$
SELECT 'a'::VARCHAR(20), 'b'::VARCHAR(20), 1::INTEGER, 'c'::VARCHAR(20), 'd'::VARCHAR(128);
$$ LANGUAGE SQL;

DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (c1 INTEGER, c2 INTEGER);
INSERT INTO t1 (c1, c2)
VALUES (2,3), (4,5);

DROP VIEW IF EXISTS v1;

person Drux    schedule 05.08.2016    source источник
comment
Не используйте select * — вместо этого выберите нужные поля. Как есть, c1 существует как в f1, так и в t1...   -  person sgeddes    schedule 05.08.2016
comment
@sgeddes Я попробую, но сообщение об ошибке цитирует c4. Отличные ответы PostgreSQL здесь, кстати.   -  person Drux    schedule 05.08.2016


Ответы (1)


Я предлагаю LATERAL объединение в SELECT запросе:

CREATE VIEW v1 AS
SELECT f.*
FROM   t1
     , f1 (t1.c1, t1.c2) AS f;  -- implicit CROSS JOIN LATERAL

Поскольку имена столбцов, определенные в определении функции, уже соответствуют тому, что вы хотите, вы также можете удалить имена столбцов из определения представления.

Создание представления в любом случае работает с ранним связыванием. Это означает, что в представление включаются только столбцы, выбранные во время создания. Если позже вы измените определение функции, чтобы она возвращала дополнительный столбец, он не будет включен в представление. (Если вы удалите или переименуете столбцы, вы нарушите представление.)

Вы можете также включить функцию возврата набора f1(..) в список SELECT, чтобы получить почти тот же эффект. Различия:

  • Функции, возвращающие наборы, в списке SELECT нарушают стандарт SQL и некоторыми не одобряются. Также не переносим на некоторые другие СУБД. Поскольку Postgres представил (стандартный SQL) LATERAL с Postgres 9.3, обычно это предпочтительнее.

  • Функция, возвращающая множество, в списке SELECT (фактически коррелированный подзапрос) эквивалентна LEFT JOIN LATERAL ... ON true, т. е. сохраняет все строки t1, даже если функция не возвращает ни одной строки. CROSS JOIN LATERAL как и выше, удаляет строки, где f1() не возвращает строки.

  • Вы можете разложить четко определенный тип строки, возвращаемый функцией, с помощью SELECT (f1(...)).*, ..., но функция может выполняться повторно для каждого столбца возвращаемого типа, а не только один раз.

Связанные и более подробные сведения:

person Erwin Brandstetter    schedule 05.08.2016