web-dev-qa-db-ja.com

新しい文字列の新しい外部キー行を挿入します

私がテーブルを持っているとしましょう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の両方を定期的に使用しているので、両方に対処する回答が理想的ですが、必須ではありません。)

1
anthropomo

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

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, ?) 
1
mouliin