如果行存在则返回id,否则返回INSERT

我在node.js中写一个函数来查询一个PostgreSQL表。
如果该行存在,我想返回行中的id列。
如果它不存在,我想插入它并返回id( insert into ... returning id )。

我一直在尝试变种的caseif else语句,似乎无法得到它的工作。

我会build议做数据库端的检查,只是返回id到nodejs。

例:

 CREATE OR REPLACE FUNCTION foo(p_param1 tableFoo.attr1%TYPE, p_param2 tableFoo.attr1%TYPE) RETURNS tableFoo.id%TYPE AS $$ DECLARE v_id tableFoo.pk%TYPE; BEGIN SELECT id INTO v_id FROM tableFoo WHERE attr1 = p_param1 AND attr2 = p_param2; IF v_id IS NULL THEN INSERT INTO tableFoo(id, attr1, attr2) VALUES (DEFAULT, p_param1, p_param2) RETURNING id INTO v_id; END IF; RETURN v_id: END; $$ LANGUAGE plpgsql; 

而不是在Node.js端(我在这个例子中使用node-postgres):

 var pg = require('pg'); pg.connect('someConnectionString', function(connErr, client){ //do some errorchecking here client.query('SELECT id FROM foo($1, $2);', ['foo', 'bar'], function(queryErr, result){ //errorchecking var id = result.rows[0].id; }; }); 

一个SQL语句中的解决scheme。 需要PostgreSQL 8.4或更高版本。
考虑下面的演示:

testing设置:

 CREATE TEMP TABLE tbl ( id serial PRIMARY KEY ,txt text UNIQUE -- obviously there is unique column (or set of columns) ); INSERT INTO tbl(txt) VALUES ('one'), ('two'); 

INSERT / SELECT命令:

 WITH v AS (SELECT 'three'::text AS txt) ,s AS (SELECT id FROM tbl JOIN v USING (txt)) ,i AS ( INSERT INTO tbl (txt) SELECT txt FROM v WHERE NOT EXISTS (SELECT * FROM s) RETURNING id ) SELECT id, 'i'::text AS src FROM i UNION ALL SELECT id, 's' FROM s; 
  • 第一个CTE v并不是绝对必要的,但是实现了只需input一次你的

  • 如果“行”存在,则第二个CTEtbl select id

  • 第三个CTE我插入 “行”到tbl如果(并且只有)它不存在,返回id

  • 最后的SELECT返回id 。 我添加了一个表示“源”的列src – “row”是否已经存在, id来自SELECT,或者“row”是新的, id

  • 这个版本应该尽可能快,因为它不需要来自tbl的额外SELECT,而是使用CTE。

为了防止在多用户环境中出现可能的竞争状况:
另外对于在Postgres 9.5或更高版本中使用新的UPSERT的更新技术:

  • SELECT或INSERT在一个容易出现竞争条件的函数中?

像这样的东西,如果你在PostgreSQL 9.1

 with test_insert as ( insert into foo (id, col1, col2) select 42, 'Foo', 'Bar' where not exists (select * from foo where id = 42) returning foo.id, foo.col1, foo.col2 ) select id, col1, col2 from test_insert union select id, col1, col2 from foo where id = 42; 

这有点长,你需要重复多次testingID,但我想不出一个涉及单个SQL语句的不同解决scheme。

如果存在id=42的行,则可写入的CTE将不会插入任何内容,因此现有的行将由第二个union部分返回。

当testing这个时,我真的以为新行会被返回两次(因此一个union而不是一个union all ),但事实certificate,第二个select语句的结果实际上是在整个语句运行之前进行评估,并没有看到新插入的行。 所以如果插入一个新行,它将从“返回”部分中取出。

 create table t ( id serial primary key, a integer ) ; insert into t (a) select 2 from ( select count(*) as s from t where a = 2 ) s where ss = 0 ; select id from t where a = 2 ;