PostgreSQL에서 복잡한 조건의 CHECK 제약 조건 구현
14656 단어 PostgreSQLSQLDB
소개
설문지의 단일 답변과 같은 "어떤 집합 중 하나만 선택할 수있는"기능을 구현했습니다.
이 제약을 DB의 테이블에서도 표현할 수 있으면 좋겠다고 생각했습니다.
궁극적으로 함수 + CHECK 제약으로 실현되었습니다.
환경
DB: PostgreSQL 12
테이블 만들기
우선 테이블을 만들어 봅시다.
ER 그림으로 하면 다음과 같이 됩니다.
-- 設問 「映画は好きですか?」 など
CREATE TABLE questions (
question_id INT NOT NULL,
question NCHAR VARYING(100) NOT NULL
);
ALTER TABLE questions ADD CONSTRAINT PK_questions PRIMARY KEY (question_id);
-- 回答の選択肢 「当てはまらない」「やや当てはまる」 など
CREATE TABLE answer_levels (
answer_level_id INT NOT NULL,
answer_level_title NCHAR VARYING(10) NOT NULL,
answer_level_score INT NOT NULL
);
ALTER TABLE answer_levels ADD CONSTRAINT PK_answer_levels PRIMARY KEY (answer_level_id);
-- 回答
CREATE TABLE responses (
question_id INT NOT NULL,
answer_level_id INT NOT NULL,
response_type INT NOT NULL CHECK(response_type < 2) -- 0: 未選択 1: 選択
);
ALTER TABLE responses ADD CONSTRAINT PK_responses PRIMARY KEY (question_id,answer_level_id);
ALTER TABLE responses ADD CONSTRAINT FK_responses_0 FOREIGN KEY (question_id) REFERENCES questions (question_id);
ALTER TABLE responses ADD CONSTRAINT FK_responses_1 FOREIGN KEY (answer_level_id) REFERENCES answer_levels (answer_level_id);
테스트 데이터 만들기
그런 다음 테스트 데이터를 만듭니다.
댓글에서 알 수 있듯이 이 테스트 데이터는 예상치 못한 데이터입니다.
하지만 이 단계에서 실행하면 할 수 있습니다.
insert into
questions(question_id, question)
values
(1, 'q-a')
, (2, 'q-b')
, (3, 'q-c')
;
insert into
answer_levels(answer_level_id, answer_level_title, answer_level_score)
values
(1, 'answer-a', 20)
, (2, 'answer-b', 40)
, (3, 'answer-c', 60)
, (4, 'answer-d', 80)
, (5, 'answer-e', 100)
;
insert into
responses(question_id, answer_level_id, response_type)
values
(1, 1, 0)
, (1, 2, 0)
, (1, 3, 0)
, (1, 4, 1)
, (1, 5, 0)
, (2, 1, 0)
, (2, 2, 0)
, (2, 3, 0)
, (2, 4, 0)
, (2, 5, 0)
, (3, 1, 0)
, (3, 2, 1)
, (3, 3, 0)
, (3, 4, 1) -- question_id=3かつresponse_type=1の回答はすでにあるので、insertできないようにしたい
, (3, 5, 0)
;
CHECK 제약 조건
그런데 곤란했습니다.
설문에 대해 단일 응답이어야 하지만, 복수의 응답을 등록할 수 있습니다.
거기서 responses 테이블에 대해서 CHECK 제약을 붙이려고 합니다.
질문 ID를 지정하여 responses.response_type = 1의 레코드 수를 얻는 함수를 만들고
CHECK 제약의 조건으로서 이용하도록(듯이) 합니다.
Postgre SQL에서는 PL/pgSQL이라는 언어를 사용하여 함수를 구현할 수 있습니다.
다음 SQL을 실행합니다.
-- 指定したquestion_idの中で、response_type=1のレコード数を返す関数
CREATE FUNCTION GetResponsedRowCount(
questionId int
) RETURNS int AS $$
DECLARE responsedRowCount int;
BEGIN
SELECT COUNT(*) INTO responsedRowCount
FROM responses
WHERE
question_id = questionId
and response_type = 1
;
RETURN responsedRowCount;
END;
$$ LANGUAGE plpgsql;
-- insert/updateする前の時点でresponse_type=1のレコードがなければOK
-- response_type=0の場合は常にOK
ALTER TABLE responses ADD CONSTRAINT ResponsedRowCountOnlyOne CHECK (GetResponsedRowCount(question_id) = 0 or response_type = 0);
CHECK 제약 조건은 insert/update 실행 전에 확인됩니다.
처음에는 insert/update 후의 상태를 정의한다고 생각했기 때문에
CHECK 제약을 다음과 같이 설정했지만 예상대로 작동하지 않았습니다.
ALTER TABLE responses ADD CONSTRAINT ResponsedRowCountOnlyOne CHECK (GetResponsedRowCount(question_id) < 2);
문서에는 다음의 설명이 있었으므로 CHECK 제약의 조건을 변경했습니다.
CHECK 절은 논리 유형의 결과를 생성하는 새 행 또는 갱신되는 행이 삽입 또는 갱신 처리를 성공적으로 수행하기 위해 만족해야 하는 표현식을 지정합니다.
요약
이상으로 조건부 CHECK 제약을 구현할 수있었습니다.
위에서 언급 한 테스트 데이터의 SQL을 실행하면 CHECK 제약 예외가 발생합니다.
다른 방법이 있으면 꼭 알고 싶습니다!
ERROR: new row for relation "responses" violates check constraint "responsedrowcountonlyone"
詳細: Failing row contains (3, 4, 1).
참고문헌
htps //w w. 포스트g sql. jp / 도쿠 멘 t / 10 / HTML / sql-c 접어서 b ぇ. HTML
h tps : // s t c ゔ ぇ rf ぉ w. 코 m / 쿠에 s 치온 s / 866061 / 콘 치오나
htps : // m / SR 사와 구치 / ms / 411801 254 66f511f1 # plpgsql % 3 % 81 % A B % 3 % 82 % 88 % 3 % 82 % 8B % 9 % 96 % A 2 % 6 %95%B0
Reference
이 문제에 관하여(PostgreSQL에서 복잡한 조건의 CHECK 제약 조건 구현), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/mahaker/items/a11c3e258f5b3c1025cc텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)