Ответ 1
Используя unnest()
и изменяющий данные CTE (требуется Postgres 9.1 или новее), это может быть простой запрос SQL:
WITH x AS (SELECT '(1,foo_book)'::book AS _book
, '{1,2,3}'::int[] AS _authors)
, y AS (
INSERT INTO book -- no column list, correct due to composite type
SELECT (x._book).*
FROM x
RETURNING book_id
)
INSERT INTO author_book (book_id, author_id)
SELECT y.book_id, unnest(x._authors)
FROM x,y; -- CROSS JOIN ok, only 1 row for x and y
Первый CTE x
предназначен только для упрощенного ввода данных и не является строго необходимым.
SQL Fiddle.
Что касается ваших вопросов:
Вопрос 1: Является ли схема функции "правильной" /имеет смысл?
Может быть проще передать базовые типы вместо составного типа book
, но это совершенно правильный подход. Однако вы должны знать свой подход к синтаксису для сложных типов. Например, обратите внимание на скобки вокруг имени в моем примере: (x._book).*
.
Функция plpgsql может выглядеть так:
CREATE OR REPLACE FUNCTION f_insert_book(_book book, _authors integer[])
RETURNS void AS
$func$
BEGIN
WITH y AS (
INSERT INTO book b
SELECT (_book).*
RETURNING b.book_id
)
INSERT INTO author_book (book_id, author_id)
SELECT y.book_id, unnest(_authors)
FROM y;
END
$func$ LANGUAGE plpgsql;
Вопрос 2: Как вставить книгу в книжную книгу? (...) или есть "более умный" способ?
Более разумный способ - разложить составной тип на (variable_name).*
.
Поскольку тип гарантированно соответствует table
(будучи полученным из него), это один из редких случаев, когда это совершенно нормально, не предоставлять список столбцов для команды INSERT
в сохраненном коде.
Вопрос 3: Как я могу назвать свою функцию insert_book?...
SELECT f_insert_book('(1,foo_book)'::book, '{1,2,3}'::int[]);
В других функциях plpgsql используйте PERFORM
вместо SELECT
, если вы не указали цель (INTO foo
) для (несуществующих).