11

node.jsにPostgreSQLテーブルをクエリする関数を書いています。
行が存在する場合は、行からid列を返します。
存在しない場合は、挿入してID(insert into ... returning id)を返します。

case私はとステートメントのバリエーションを試してきましたがif else、それを機能させることができないようです。

4

4 に答える 4

10

単一のSQLステートメントのソリューション。ただし、 PostgreSQL8.4以降が必要です
次のデモを検討してください。

テスト設定:

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;
  • 最初のCTEvは厳密には必要ありませんが、を1回だけ入力する必要があることを実現します。

  • 2番目のCTEは、「行」が存在する場合にidfromを選択します。tbl

  • 3番目の CTEiは、「行」tblが存在しない場合(および存在しない場合のみ)に「行」を挿入し、を返しidます。

  • ファイナルSELECTはを返しますidsrc「ソース」を示す列を追加しましたid。「行」が既存でSELECTに由来するのか、「行」が新しいのか、またそうですid

  • tblこのバージョンは、からの追加のSELECTを必要とせず、代わりにCTEを使用するため、可能な限り高速である必要があります。

マルチユーザー環境で発生する可能性のある競合状態に対してこれを安全にするには:Postgres 9.5以降
の新しいUPSERTを使用した最新の技術についても:

于 2012-04-09T20:53:00.773 に答える
8

データベース側でチェックを行い、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;      

  };

});
于 2012-04-07T20:28:05.360 に答える
2

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は何も挿入しないため、既存の行は2番目の和集合部分によって返されます。

これをテストしたとき、実際には新しい行が2回返されると思いましたが(したがって、でunionはありませんunion all)、2番目のselectステートメントの結果は、ステートメント全体が実行される前に実際に評価され、新しく挿入された行は表示されません。したがって、新しい行が挿入された場合、それは「戻り」部分から取得されます。

于 2012-04-07T21:31:12.117 に答える
0
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
;
于 2012-04-07T20:29:46.287 に答える