如何将带有数组字段的表类型传递给 postgresql 中的函数

ind*_*ago 8 postgresql datatypes functions postgresql-9.1 array

我有一张桌子叫书

CREATE TABLE book
(
  id smallint NOT NULL DEFAULT 0,       
  bname text,       
  btype text,
  bprices numeric(11,2)[],
  CONSTRAINT key PRIMARY KEY (id )
)
Run Code Online (Sandbox Code Playgroud)

和一个函数 save_book

CREATE OR REPLACE FUNCTION save_book(thebook book)
  RETURNS text AS
$BODY$
DECLARE 
myoutput text :='Nothing has occured';
BEGIN

    update book set 
    bname=thebook.bname,
    btype=thebook.btype,bprices=thebook.bprices  WHERE id=thebook.id;

    IF FOUND THEN
        myoutput:= 'Record with PK[' || thebook.id || '] successfully updated';
        RETURN myoutput;
    END IF;

    BEGIN
        INSERT INTO book values(thebook.id,thebook.bname,thebook.btype,
        thebook.bprices);
        myoutput:= 'Record successfully added';           
    END;
 RETURN myoutput;

    END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
Run Code Online (Sandbox Code Playgroud)

现在当我调用函数时

SELECT save_book('(179,the art of war,fiction,{190,220})'::book);
Run Code Online (Sandbox Code Playgroud)

我得到了错误

ERROR: malformed array literal: "{190"
SQL state: 22P02
Character: 18
Run Code Online (Sandbox Code Playgroud)

我不明白,因为我没有看到数组格式有任何错误,有什么帮助吗?

Chr*_*ers 7

这种事情变得复杂了。我现在正在做一些相关的项目。基本的调整是 PostgreSQL 使用一种在元组表示中内部使用双引号来表示文字值的格式,因此:

SELECT save_book('(179,the art of war,fiction,"{190,220}")'::book);
Run Code Online (Sandbox Code Playgroud)

应该管用。本质上,一个巧妙的技巧是创建一个 csv 并包含在元组或数组标识符中。最大的问题是您必须处理转义(根据需要在每个级别加倍引号)。所以下面是完全等效的:

SELECT save_book('(179,"the art of war","fiction","{""190"",""220""}")'::book);
Run Code Online (Sandbox Code Playgroud)

第二种方法是使用行构造函数:

SELECT save_book(row(179,'the art of war','fiction', array[190,220])::book);
Run Code Online (Sandbox Code Playgroud)

第一个解决方案的明显优势是能够利用现有的编程框架进行 CSV 生成和转义。第二个是 SQL 中最干净的。它们可以混合搭配。


Erw*_*ter 6

如果您想知道行类型的正确语法,请询问 Postgres。它应该知道:

SELECT b FROM book b LIMIT 1;  -- or: WHERE id = 179;
Run Code Online (Sandbox Code Playgroud)

它将以有效格式返回您的行的文本表示:

(179,"the art of war",fiction,"{190,220}")
Run Code Online (Sandbox Code Playgroud)
  • 列的值表示为一个不带引号的逗号分隔列表,用括号括起来。

  • 如果可能存在歧义,则在值周围使用双引号 - 包括带有空格的文本。虽然在这种特殊情况下,周围的双引号"the art of war"是可选的,但周围的双引号"{190,220}"对于数组来说是必需的。

将字符串括在单引号中,修改并测试:

SELECT '(333,the art of war,fiction,"{191,220,235}")'::book
Run Code Online (Sandbox Code Playgroud)

功能回顾

考虑我们在前面的相关问题下讨论的内容:UPSERT 函数中的复合类型问题

一个单独的BEGIN .. END;如果你想赶上)是唯一有用EXCEPTIONINSERT可能引起。由于具有异常块会带来一些开销,因此有一个可能永远不会进入的单独块是有意义的:

CREATE OR REPLACE FUNCTION save_book(thebook book)
  RETURNS text AS
$BODY$
BEGIN
   UPDATE book
   SET    bname =   thebook.bname
         ,btype =   thebook.btype
         ,bprices = thebook.bprices
   WHERE  id = thebook.id;

   IF FOUND THEN
      RETURN format('Record with PK[%s] successfully updated', thebook.id);
   END IF;

   BEGIN
      INSERT INTO book SELECT (thebook).*;
      RETURN format('Record with PK[%s] successfully inserted', thebook.id);

   EXCEPTION WHEN unique_violation THEN
      UPDATE book
      SET    bname =   thebook.bname
            ,btype =   thebook.btype
            ,bprices = thebook.bprices
      WHERE  id = thebook.id;
   END;

   RETURN format('Record with PK[%s] successfully updated', thebook.id);
END
$BODY$  LANGUAGE plpgsql
Run Code Online (Sandbox Code Playgroud)

否则,简化

CREATE OR REPLACE FUNCTION save_book(thebook book)
  RETURNS text AS
$BODY$
BEGIN
   UPDATE book
   SET    bname =   thebook.bname
         ,btype =   thebook.btype
         ,bprices = thebook.bprices
   WHERE  id = thebook.id;

   IF FOUND THEN
      RETURN format('Record with PK[%s] successfully updated', thebook.id);
   END IF;

   INSERT INTO book SELECT (thebook).*;
   RETURN format('Record with PK[%s] successfully inserted', thebook.id);
END
$BODY$  LANGUAGE plpgsql
Run Code Online (Sandbox Code Playgroud)

我也简化了你的INSERT陈述。在给定的情况下,从 INSERT 中省略列列表是安全的。