他のテーブルを参照するテーブルがあります:
CREATE TABLE scratch
(
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
rep_id INT NOT NULL REFERENCES reps,
term_id INT REFERENCES terms
);
CREATE TABLE reps (
id SERIAL PRIMARY KEY,
rep TEXT NOT NULL UNIQUE
);
CREATE TABLE terms (
id SERIAL PRIMARY KEY,
terms TEXT NOT NULL UNIQUE
);
name、rep、およびtermsの値を指定して、新しいレコードをスクラッチに追加したいと考えています。つまり、対応するrep_idもterm_idもありません。
現在、私が持っている唯一のアイデアは次のとおりです。
insert into scratch (name, rep_id, term_id)
values ('aaa', (select id from reps where rep='Dracula' limit 1), (select id from terms where terms='prepaid' limit 1));
私の問題はこれです。パラメータ化されたクエリ API を使用しようとしています (node-postgres パッケージを使用するノードから)。挿入クエリは次のようになります。
insert into scratch (name, rep_id, term_id) values ($1, $2, $3);
次に、$1、$2、および $3 の値の配列が別の引数として渡されます。最後に、パラメーター化されたクエリに慣れたら、それらを準備済みステートメントに昇格させて、データベースをクエリする最も効率的で安全な方法を利用することを考えます。
ただし、さまざまなテーブルをサブクエリする必要がある私の例でこれを行うにはどうすればよいか、私は困惑しています。
PS私はPostgreSQL 9.2を使用していますが、PostgreSQL固有のソリューションに問題はありません。
編集1
C:\Users\markk>psql -U postgres
psql (9.2.4)
WARNING: Console code page (437) differs from Windows code page (1252)
8-bit characters might not work correctly. See psql reference
page "Notes for Windows users" for details.
Type "help" for help.
postgres=# \c dummy
WARNING: Console code page (437) differs from Windows code page (1252)
8-bit characters might not work correctly. See psql reference
page "Notes for Windows users" for details.
You are now connected to database "dummy" as user "postgres".
dummy=# DROP TABLE scratch;
DROP TABLE
dummy=# CREATE TABLE scratch
dummy-# (
dummy(# id SERIAL NOT NULL PRIMARY KEY,
dummy(# name text NOT NULL UNIQUE,
dummy(# rep_id integer NOT NULL,
dummy(# term_id integer
dummy(# );
NOTICE: CREATE TABLE will create implicit sequence "scratch_id_seq" for serial column "scratch.id"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "scratch_pkey" for table "scratch"
NOTICE: CREATE TABLE / UNIQUE will create implicit index "scratch_name_key" for table "scratch"
CREATE TABLE
dummy=# DEALLOCATE insert_scratch;
ERROR: prepared statement "insert_scratch" does not exist
dummy=# PREPARE insert_scratch (text, text, text) AS
dummy-# INSERT INTO scratch (name, rep_id, term_id)
dummy-# SELECT $1, r.id, t.id
dummy-# FROM reps r, terms t
dummy-# WHERE r.rep = $2 AND t.terms = $3
dummy-# RETURNING id, name, $2 rep, $3 terms;
PREPARE
dummy=# DEALLOCATE insert_scratch2;
ERROR: prepared statement "insert_scratch2" does not exist
dummy=# PREPARE insert_scratch2 (text, text, text) AS
dummy-# INSERT INTO scratch (name, rep_id, term_id)
dummy-# VALUES ($1, (SELECT id FROM reps WHERE rep=$2 LIMIT 1), (SELECT id FROM terms WHERE terms=$3 LIMIT 1))
dummy-# RETURNING id, name, $2 rep, $3 terms;
PREPARE
dummy=# EXECUTE insert_scratch ('abc', 'Snowhite', '');
id | name | rep | terms
----+------+-----+-------
(0 rows)
INSERT 0 0
dummy=# EXECUTE insert_scratch2 ('abc', 'Snowhite', '');
id | name | rep | terms
----+------+----------+-------
1 | abc | Snowhite |
(1 row)
INSERT 0 1
dummy=# EXECUTE insert_scratch ('abcd', 'Snowhite', '30 days');
id | name | rep | terms
----+------+----------+---------
2 | abcd | Snowhite | 30 days
(1 row)
INSERT 0 1
dummy=# EXECUTE insert_scratch2 ('abcd2', 'Snowhite', '30 days');
id | name | rep | terms
----+-------+----------+---------
3 | abcd2 | Snowhite | 30 days
(1 row)
INSERT 0 1
dummy=#
編集2
rep_id
オプションであるにもかかわらず、必須であるという事実を利用してterms_id
、次のバージョンの INSERT-SELECT を使用できます。
PREPARE insert_scratch (text, text, text) AS
INSERT INTO scratch (name, rep_id, term_id)
SELECT $1, r.id, t.id
FROM reps r
LEFT JOIN terms t ON t.terms = $3
WHERE r.rep = $2
RETURNING id, name, $2 rep, $3 terms;
ただし、このバージョンには 2 つの問題があります。
terms
欠落している値 (つまり '') と無効なterms
値 (つまり、terms テーブルから完全に欠落している空でない値)は区別されません。どちらも欠落語として扱われます。(しかし、2 つのサブクエリを持つ INSERT には同じ問題があります)- バージョンは、が必要であるという事実に依存し
rep
ます。しかし、それrep_id
もオプションだったとしたらどうでしょうか?
編集3
項目 2 の解決策を見つけました - 必要な担当者への依存を排除します。さらに、WHEREステートメントを使用すると、担当者が無効な場合にSQLが失敗しないという問題があります.0行を挿入するだけですが、この場合は明示的に失敗したいと思います。私の解決策は、ダミーの1行CTEを使用するだけです:
PREPARE insert_scratch (text, text, text) AS
WITH stub(x) AS (VALUES (0))
INSERT INTO scratch (name, rep_id, term_id)
SELECT $1, r.id, t.id
FROM stub
LEFT JOIN terms t ON t.terms = $3
LEFT JOIN reps r ON r.rep = $2
RETURNING id, name, rep_id, term_id;
担当者が見つからないか無効な場合、この sql は NULL を rep_id フィールドに挿入しようとし、フィールドがNOT NULL
エラーになるため、正確に必要なものです。さらに、rep をオプションにすることにした場合、問題ありません。同じ SQL がその場合にも機能します。