1

我想在 PostgreSQL中做这样的事情。

我试过这个:

CREATE or replace FUNCTION create_patient(_name text, _email text, _phone text
     , _password text
     , _field1 text, _field2 text, _field3 timestamp, _field4 text
     , OUT _pid integer, OUT _id integer
    ) RETURNS record AS
$$
DECLARE
    _id integer;
    _type text;
    _pid integer;
BEGIN
    _type := 'patient';

    INSERT into patients (name, email, phone, field1, field2, field3)
    VALUES (_name, _email, _phone, _field1, _field2, _field3)
    RETURNING id into _pid;

    INSERT into users (username, password, type, pid, phone, language)
    VALUES (_email, _password, _type, _pid, _phone, _field4)
    RETURNING id into _id;
END;
$$ LANGUAGE plpgsql;

但是在很多情况下,我不想指定其中的一些field1 / field2 / field3 / field4并且希望未指定的字段使用表中的默认值。目前这是不可能的,因为要调用此函数,我需要指定所有字段。

有没有一种简单的方法可以在 PL/pgSQL 中创建包装程序INSERT,我可以在其中指定要插入的字段?

4

1 回答 1

6

对函数参数和动态 SQL使用默认值...

演示功能

CREATE OR REPLACE FUNCTION create_patient(
     _name     text      = NULL  -- always updated, NULL if not provided
    ,_email    text      = NULL
    ,_phone    text      = NULL
    ,_password text      = NULL
    ,_field1   text      = NULL  -- variable parameters
    ,_field2   text      = NULL
    ,_field3   timestamp = NULL
    ,_language text      = NULL
    ,_cols     text[]    = '{field1,field2,field3,language}'
    ,OUT _pid  text
    ,OUT _id   int)
  RETURNS record AS
$func$
BEGIN
   EXECUTE format(
   'INSERT INTO patients (field1, field2, field3, name, email, phone)
    VALUES               (%s,     %s,     %s,     $4,   $5,    $6   )
    RETURNING id'
       ,CASE WHEN 'field1' = ANY(_cols) THEN '$1' ELSE 'DEFAULT' END
       ,CASE WHEN 'field2' = ANY(_cols) THEN '$2' ELSE 'DEFAULT' END
       ,CASE WHEN 'field3' = ANY(_cols) THEN '$3' ELSE 'DEFAULT' END)
    INTO  _pid                       -- return value, also used in 2nd insert
    USING _field1, _field2, _field3, _name, _email, _phone;

   EXECUTE format(
   'INSERT INTO users (username, password, type,        pid, phone, language)
    VALUES            ($1,       $2,       $$patient$$, $3,  $4,    %s      )
    RETURNING id'
      ,CASE WHEN 'language' = ANY(_cols) THEN '$4' ELSE 'DEFAULT' END)
    INTO  _id                        -- return value
    USING _email, _password, _pid, _phone, _language;
END
$func$  LANGUAGE plpgsql;

称呼

SELECT * FROM create_patient('myname','myemail','myphone','mypassword'
,'myfield1','myfield2',NULL,'English','{field2,language,field1}'::text[]);

由于该函数使用命名参数并且每个参数都有一个默认值,因此您甚至可以这样调用它:

SELECT * FROM create_patient(_name := 'myname');

如果需要一些非空值,可能不适用于您的表,但可以证明一旦您提供命名参数,您就可以省略任何具有默认值的参数。省略的参数采用声明的默认值(不要与列默认值混淆)。此相关答案中的更多内容:
具有可变数量输入参数的函数

要点

  • 使用command的DEFAULT关键字INSERT。它使系统插入表的默认列。
    另一种方法是仅列出INSERT行中获得相应项目的VALUES列。

  • 必须使用动态 SQL 并EXECUTE操纵语句本身,而不仅仅是值。

  • “摆动柱”是field1field3language其余的是根据定义硬连线的。根据需要变化。

  • 我的函数适用于所有情况,您甚至可以提供一个NULL值而不是列默认值。这需要一个参数_cols来提供要插入哪些列的信息。

  • 如果声明了所有涉及的列NOT NULL- 尚未澄清 - 您可以简化:传递NULL任何应该获得列默认值的列并调整CASE语句。

  • 如果省略_cols,将插入所有字段。由于_cols是最后一个IN参数并且具有默认值,您可以随时省略它。

  • 我使用USINGfor 子句EXECUTE将参数作为传递,并防止使用动态构建的查询字符串进行 SQL 注入。

  • format()用来简化语句汇编并避免多重赋值。PL/pgSQL 更便宜。

  • 不要在函数体DECLARE _id_pid使用,因为它们是由OUT标头中的参数声明并自动返回的。

  • type您可以直接在INSERT语句中插入一个常量值。这样您就不需要任何变量并保存额外的分配。

  • 使用 PostgreSQL 9.1测试,但应该适用于自 8.4 以来的所有版本——除了format()9.1 引入的版本。

于 2012-06-23T15:12:36.113 回答