web-dev-qa-db-ja.com

競合時のPostgresが複合主キーを更新する

ユーザーが質問に答えるテーブルがあります。ルールは、ユーザーが多くの質問に答えることができるか、または多くのユーザーが1つの質問に答えることができるが、ユーザーは特定の質問に1回だけ答えることができるということです。ユーザーがもう一度質問に答えた場合は、古い質問を置き換えるだけです。一般に、一意の列を処理しているときに、競合が発生しても更新が機能します。このシナリオでは、列person_idおよびquestion_idを一意にすることはできません。ただし、この2つの組み合わせは常に一意です。競合時に更新する挿入ステートメントを実装するにはどうすればよいですか?

CREATE TABLE "answer" (
  "person_id" integer NOT NULL REFERENCES person(id), 
  "question_id" integer NOT NULL REFERENCES question(id) ON DELETE CASCADE, /* INDEXED */
  "answer" character varying (1200) NULL,
  PRIMARY KEY (person_id, question_id) 
);
10
pewpewlasers

両方のキーをON CONFLICT句:

INSERT INTO answer VALUES (1,1,'q1') 
ON CONFLICT (person_id,question_id) 
DO UPDATE SET answer = EXCLUDED.answer; 

例:

INSERT INTO answer VALUES (1,1,'q1') 
ON CONFLICT (person_id,question_id) 
DO UPDATE SET answer = EXCLUDED.answer;             

SELECT * FROM answer;
 person_id | question_id | answer 
-----------+-------------+--------
         1 |           1 | q1
(1 Zeile)

INSERT INTO answer VALUES (1,1,'q1-UPDATED') 
ON CONFLICT (person_id,question_id) 
DO UPDATE SET answer = EXCLUDED.answer;             

SELECT * FROM answer;
 person_id | question_id |   answer   
-----------+-------------+------------
         1 |           1 | q1-UPDATED
(1 Zeile)
15
Jim Jones

また、プライマリをテーブルの外部で定義することもでき、そこに含まれるすべての列を書き直す必要はありません。

CREATE TABLE "answer" (
  "person_id" integer NOT NULL REFERENCES person(id), 
  "question_id" integer NOT NULL REFERENCES question(id) ON DELETE CASCADE, /* INDEXED */
  "answer" character varying (1200) NULL);

ALTER TABLE "answer" ADD CONSTRAINT answer_pk PRIMARY KEY (person_id, question_id);

その後:

INSERT INTO answer VALUES (1,1,'q1') ON CONFLICT ON CONSTRAINT answer_pk DO UPDATE SET answer = EXCLUDED.answer;

将来的に制約が変更された場合、これを反映するために挿入ステートメントを手動で調整する必要はありません。