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

gopyfrb3  于 2023-03-12  发布在  PostgreSQL
关注(0)|答案(4)|浏览(227)

我在node.js中编写了一个函数来查询PostgreSQL表。
如果该行存在,我想从该行返回id列。
如果它不存在,我想插入它并返回id(insert into ... returning id)。
我一直在尝试caseif else语句的变体,但似乎无法使其工作。

pobjuy32

pobjuy321#

Postgres 9.5或更高版本

使用“UPSERT”,以确保在多用户环境中避免可能的争用情况。请参阅:

  • 函数中的SELECT或INSERT是否容易出现争用情况?

Postgres 9.4或更早版本

单个SQL语句中的解决方案。测试设置:

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不是严格必需的,但可以实现只需输入一次的目的。
  • 如果“行”存在,则第二个CTE stbl中选择id
  • 第三个CTE itbl中插入“行”,当(且仅当)它不存在时,返回id
  • 最后一个SELECT返回id,我添加了一列src来指示“源”--“行”是否已经存在并且id来自SELECT,或者“行”是新的并且id也是新的。
  • 此版本应尽可能快,因为它不需要tbl的额外SELECT,而是使用CTE。
dgenwo3n

dgenwo3n2#

我建议在数据库端进行检查,并将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-side(我在这个例子中使用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;      

  };

});
hs1rzwqc

hs1rzwqc3#

如果您使用的是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;

它有点长,您需要多次重复id来进行测试,但我想不出涉及单个SQL语句的其他解决方案。
如果存在id=42的行,则可写CTE不会插入任何内容,因此第二个联合部分将返回现有行。
在测试这个的时候,我实际上认为新行会被返回两次(因此是union而不是union all),但结果是第二个select语句的结果实际上是在整个语句运行之前被求值的,它看不到新插入的行。所以如果插入了一个新行,它将从“返回”部分被取出。

yebdmbv4

yebdmbv44#

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 s.s = 0
;
select id
from t
where a = 2
;

相关问题