node.jsにPostgreSQLテーブルをクエリする関数を書いています。
行が存在する場合は、行からid列を返します。
存在しない場合は、挿入してID(insert into ... returning id
)を返します。
case
私はとステートメントのバリエーションを試してきましたがif else
、それを機能させることができないようです。
node.jsにPostgreSQLテーブルをクエリする関数を書いています。
行が存在する場合は、行からid列を返します。
存在しない場合は、挿入してID(insert into ... returning id
)を返します。
case
私はとステートメントのバリエーションを試してきましたがif else
、それを機能させることができないようです。
単一の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は、「行」が存在する場合にid
fromを選択します。tbl
3番目の CTEiは、「行」tbl
が存在しない場合(および存在しない場合のみ)に「行」を挿入し、を返しid
ます。
ファイナルSELECT
はを返しますid
。src
「ソース」を示す列を追加しましたid
。「行」が既存でSELECTに由来するのか、「行」が新しいのか、またそうですid
。
tbl
このバージョンは、からの追加のSELECTを必要とせず、代わりにCTEを使用するため、可能な限り高速である必要があります。
マルチユーザー環境で発生する可能性のある競合状態に対してこれを安全にするには:Postgres 9.5以降
の新しいUPSERTを使用した最新の技術についても:
データベース側でチェックを行い、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;
};
});
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ステートメントの結果は、ステートメント全体が実行される前に実際に評価され、新しく挿入された行は表示されません。したがって、新しい行が挿入された場合、それは「戻り」部分から取得されます。
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
;