使用DEFAULT或INTEGER值作为SQL函数的参数

时间:2015-05-10 19:52:24

标签: sql postgresql function concurrency sql-insert

我正在创建一个SQL函数来执行智能插入并管理多对多的表(触发器不起作用,因为一个值是可变的)。

以下是示例的最小集:

link_foo_to_bar (foo_id int, bar_id int)
foo (id serial, name character varying)

这是我的sql函数:

CREATE FUNCTION smart_insert (bar_id integer, foo_id integer, name character varying name)
RETURNS void AS $body$
INSERT INTO foo VALUES (foo_id, name);
INSERT INTO link_foo_to_bar (CURRVAL('foo_id_seq'), bar_id);
$body$ LANGUAGE sql;

现在问题是foo_idserial,因此它可以获得DEFAULTINTEGER作为传入值。该函数不接受DEFAULT关键字。我应该如何更改函数以允许这种变化的参数?使用文字类型?

2 个答案:

答案 0 :(得分:3)

以下是在PostgreSQL中指定DEFAULT函数参数的方法:

CREATE FUNCTION smart_insert (
  name character varying,
  bar_id integer, 
  foo_id integer = -1
)
RETURNS void AS $body$
INSERT INTO foo VALUES (
  CASE foo_id WHEN -1 THEN NEXTVAL('foo_id_seq') ELSE foo_id END, 
  name
);
INSERT INTO link_foo_to_bar (
  CASE foo_id WHEN -1 THEN CURRVAL('foo_id_seq') ELSE foo_id END,
  bar_id
);
$body$ LANGUAGE sql;

以下是您可以调用的方式:

-- with an explicit foo_id:
smart_insert('abc', 1, 2);

-- applying the default value:
smart_insert('abc', 1);

...但是,关于插入serial类型,你应该考虑一些useful feedback from Erwin,你应该小心。

答案 1 :(得分:3)

首先,始终为持久性INSERT语句提供显式目标列。如果你把它放在一个函数中,然后更改表的列,你的函数就会中断,或者 更糟 不会中断并做出意想不到的事情。

接下来,您不应该在serial 列中输入值。这导致了关键的冲突。您需要使基础SEQUENCEserial列中当前最高的值同步,这对于并发写入加载可能很棘手。不要这样做。

我怀疑你确实有 SELECT - 或 - INSERT 的案例。这意味着,如果给定name已存在于fooSELECT已关联foo_id,则INSERT为新行,并使用动态分配的新{{1} }}

问题似乎与函数参数的默认值无关,就像SQL foo_id语句中的关键字DEFAULT一样(完全不同的东西!)当你可以使用时一个功能,你不需要一个功能。

没有并发写入负载

您可以使用数据修改CTE(带或不带功能):

INSERT

更多详情:

或者,如果你真的必须强行手册WITH val(name, bar_id) AS (SELECT 'given_name'::varchar, 7) -- type cast needed for varchar, but numeric constant typed automatically , sel AS (SELECT f.foo_id, v.bar_id FROM foo f JOIN val v USING (name) , ins AS (INSERT INTO foo(name) SELECT name FROM val WHERE NOT EXISTS (SELECT 1 FROM sel) -- only if not found RETURNING foo_id) INSERT INTO link_foo_to_bar(foo_id, bar_id) SELECT foo_id, bar_id FROM sel UNION ALL SELECT foo_id, bar_id FROM ins, val; ...... 此变体包含在SQL函数中:

foo_id

致电... like Lukas instructed.

关键元素是CREATE FUNCTION smart_insert( _name varchar , _bar_id integer , _foo_id integer = NULL) -- no default necessary, but NULL for convenience RETURNS void AS $func$ WITH sel AS (SELECT f.foo_id -- existing foo_id overrules FROM foo f WHERE f.name = _name) , ins AS (INSERT INTO foo(foo_id, name) SELECT COALESCE(_foo_id, nextval('foo_id_seq')), _name WHERE NOT EXISTS (SELECT 1 FROM sel) -- only if not found RETURNING foo_id) INSERT INTO link_foo_to_bar(foo_id, bar_id) SELECT foo_id, _bar_id FROM sel UNION ALL SELECT foo_id, _bar_id FROM ins; $func$ LANGUAGE sql; :如果你传递COALESCE(_foo_id, nextval('foo_id_seq')它会被使用,否则默认是从序列中提取的。无论您选择什么,它都会随_foo_id一起返回并插入该行。

使用并发写入负载

Postgres 9.4

您可以使用此相关答案中的函数来处理并发问题(并提供更多解释):

RETURNING foo_id

Postgres 9.5

仍处于开发阶段,但几天前新的UPSERT feature has been committed(耶!)并将在下一个版本中发布。但请不要屏住呼吸,计划在2015年底发布。

INSERT INTO link_foo_to_bar(foo_id, bar_id)
VALUES (f_foo_id('given_name'), 7);

devel手册中的详细信息:
http://www.postgresql.org/docs/devel/static/sql-insert.html

除此之外:几乎可以肯定一个触发器也是可能的。但不一定是更好的选择。