Postgresql 将记录作为函数参数PL/pgSQL传递

Postgresql 将记录作为函数参数PL/pgSQL传递,postgresql,types,plpgsql,common-table-expression,sql-insert,Postgresql,Types,Plpgsql,Common Table Expression,Sql Insert,首先,我对pl/pgsql非常陌生。项目需要它 我被这个(简化的)问题困住了 我的数据库模式具有n到m关系(作者、书籍、作者和书籍) 现在我想要一个pl/psgsql函数insert\u book。(我知道所有作者都肯定已经在author表中,所以我只想传递他们的主键) 这个函数大纲就是我想要的 create or replace function insert_book(book_to_insert book, authors integer[]) returns void as $$

首先,我对pl/pgsql非常陌生。项目需要它

我被这个(简化的)问题困住了

我的数据库模式具有n到m关系(作者、书籍、作者和书籍)

现在我想要一个pl/psgsql函数insert\u book。(我知道所有作者都肯定已经在author表中,所以我只想传递他们的主键)

这个函数大纲就是我想要的

 create or replace function insert_book(book_to_insert book, authors integer[])
  returns void as $$
begin
    -- insert book into table books
    -- for each author add an entry to author_books table
end;
 $$ language plpgsql;
作为论据,我想通过一本书的类型和作者的记录。但这到底是怎么回事?我在谷歌上搜索了很多次,但似乎无法找到答案

问题1:功能大纲“正确”吗/有意义吗

问题2:如何将记录簿插入表格簿?我是否必须检查书籍的所有字段(标题、isbn、出版商等)并将它们添加到INSERT INTO语句中,还是有一种“更聪明”的方法

问题3:如何调用函数insert\u book?我在这里找到了这个例子(http://dbaspot.com/postgresql/206142-passing-record-function-argument-pl-pgsql.html),但这对我没什么帮助。出于测试目的,我正在使用shell,但稍后我们将使用Java和JDBC

非常感谢您的帮助。

使用和(需要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
仅用于简化数据输入,并不严格需要

关于你的问题: 问题1:功能大纲“正确”吗/有意义吗

传递基类型可能比传递复合类型
book
更容易,但这是一种非常有效的方法。不过,您必须了解复杂类型的语法。例如,请注意我的示例中名称周围的括号:
(x.\u 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:如何将记录簿插入表簿?(…)还是有“更聪明”的方法

更聪明的方法是使用
(变量名称)分解复合类型。*

由于类型保证与
(从表中派生)匹配,这是极少数情况之一,在这种情况下,在持久化代码中不为
插入
命令提供列列表是完全可以的

问题3:我如何调用我的函数insert_book

在其他plpgsql函数中,如果没有为(不存在的)结果提供目标(
到foo
),请使用而不是
选择

传递JSON数据类型(Postgresql 9.2或更高版本):


我得多读一些关于CTE的书。我必须承认我不理解他们。我看过你的几篇文章,其中你使用了它们,它们似乎简化了你的解决方案,但这并不是在处理问题时考虑使用的东西。@DavidS:对于简单的情况,普通的子查询通常要快一点。但CTE对于更复杂的操作或在多个子查询中重用结果非常有用。数据修改CTE非常适合同时操作多个表,将它们绑定在一起。递归CTE基本上是使用纯SQL进行递归处理的唯一方法。非常强大-但并不总是容易理解。是一个让你开始的好地方。谢谢欧文。。。好建议。我需要再读一次手册,并玩一些例子。对于更复杂的操作,我倾向于使用“做”语句,这些语句允许我以大脑似乎“得到”的方式做事情。我确信这是因为在此期间我编写的过程代码比SQL多。:)再次感谢,你好,欧文。非常感谢您的详细回答。它帮助了我很多,而且它像一个符咒一样工作:)。
SELECT f_insert_book('(1,foo_book)'::book, '{1,2,3}'::int[]);
CREATE OR REPLACE FUNCTION f_insert_book(_book json, _authors json)
   RETURNS void AS 
$$
BEGIN
-- insert book into table books
Insert into books values select * from json_populate_recordset(null:book, _book);
    -- for each author add an entry to author_books table
Insert into authors values select * from json_populate_recordset(null:authors, _authors);
end;
$$ language plpgsql;