私がテーブルを持っているとしましょうnew_data
このような:
ndx | someval | somemachine
serial | integer | integer
----------------------------
そして、私が参照テーブルを持っているとしましょうnew_data_someval_ref
:
someval | someval_str
integer | varchar
---------------------
(ここで、明らかに、new_data_someval_ref.somevalはnew_data.somevalの外部キーです)
そして、私はそのような挿入をしたい:
INSERT INTO new_data
(someval, machine)
VALUES (
(SELECT someval
FROM new_data_someval_ref
WHERE someval_str = ?),
?)
ただし、参照テーブルに一致するものがない場合は、新しい文字列とインクリメントされたsomeval
を含む行を追加してから、上記の挿入を実行します。
だから私の質問は、これを行うためのクリーンな、おそらく制約ベースの方法がありますか?
(私はPostgresとSQL Serverの両方を定期的に使用しているので、両方に対処する回答が理想的ですが、必須ではありません。)
someval
が実際には serial
のような自動生成された列であると仮定します。
Postgresでは、これ data-modifying CTE (writeable CTE)が仕事をします:
WITH sel AS (
SELECT r.someval, i.someval_str, i.machine
FROM (SELECT ?, ?) AS i(someval_str, machine)
LEFT JOIN new_data_someval_ref r USING (someval_str)
)
, ins AS (
INSERT INTO new_data_someval_ref (someval_str)
SELECT DISTINCT someval_str FROM sel WHERE someval IS NULL
RETURNING someval, someval_str
)
INSERT INTO new_data (someval, machine)
SELECT COALESCE(sel.someval, ins.someval), sel.machine
FROM sel
LEFT JOIN ins USING (someval_str);
すべてが標準SQLであり、すべてSQLServerに同じように実装されているかどうかはわかりません。
複数の入力行と十分な説明があるクエリの場合:
同時負荷が大きい場合、競合状態の小さな可能性があります。 (1つのステートメントですべてを実行すると、時間枠が最小化されます。)その場合:
2つのブロックに分割する必要があると思います(ストアドプロシージャの方が簡単です)
DECLARE @SomeVal INT
SET @SomeVal = Isnull((SELECT someval
FROM new_data_someval_ref
WHERE someval_str = 'WhatEverTheValueIs'), 0)
IF @SomeVal = 0
BEGIN
INSERT INTO new_data_someval_ref
(someval_str)
VALUES ('WhatEverTheValueIs')
SET @SomeVal = Scope_identity()
END
INSERT INTO new_data
(someval, machine)
VALUES (@SomeVal, ?)