对函数参数和动态 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
操纵语句本身,而不仅仅是值。
“摆动柱”是field1
和field3
,language
其余的是根据定义硬连线的。根据需要变化。
我的函数适用于所有情况,您甚至可以提供一个NULL
值而不是列默认值。这需要一个参数_cols
来提供要插入哪些列的信息。
如果声明了所有涉及的列NOT NULL
- 尚未澄清 - 您可以简化:传递NULL
任何应该获得列默认值的列并调整CASE
语句。
如果省略_cols
,将插入所有字段。由于_cols
是最后一个IN
参数并且具有默认值,您可以随时省略它。
我使用USING
for 子句EXECUTE
将参数作为值传递,并防止使用动态构建的查询字符串进行 SQL 注入。
我format()
用来简化语句汇编并避免多重赋值。PL/pgSQL 更便宜。
不要在函数体DECLARE
_id
中_pid
使用,因为它们是由OUT
标头中的参数声明并自动返回的。
type
您可以直接在INSERT
语句中插入一个常量值。这样您就不需要任何变量并保存额外的分配。
使用 PostgreSQL 9.1测试,但应该适用于自 8.4 以来的所有版本——除了format()
9.1 引入的版本。