postgesqlでは、unique indexによってユニーク制約できますが、
値の一部(例:timestampの日付部分)でユニーク制約したい場合、
unique index では対応できません。
この場合は、trigger を利用する必要があります。
以下のtest テーブルを例とします。
CREATE TABLE public.test
(
id integer NOT NULL,
name text COLLATE pg_catalog."default" NOT NULL,
age integer NOT NULL,
created_at timestamp with time zone NOT NULL
)
同じ日付でname,ageも同じレコードを insert するとエラーにしたい場合、以下のトリガーを定義します。
まず、トリガー用の function を定義します。
対象レコードがすでに存在する場合、unique_violation を発生するようにします。
CREATE OR REPLACE FUNCTION trigger_unique_check_func()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
BEGIN
IF EXISTS (SELECT name from test WHERE name = NEW.name
and age = NEW.age
and created_at::date = NEW.created_at::date ) THEN
RAISE EXCEPTION unique_violation;
END IF;
RETURN NEW;
END $$;
次は、トリガーを定義します。
CREATE TRIGGER trigger_unique_check
BEFORE INSERT
ON test
FOR EACH ROW
EXECUTE PROCEDURE trigger_unique_check_func();
試験:
まず、以下のSQLを実行します。
INSERT INTO test VALUES(1, 'a', 1, '2020-12-02 13:14:15');
結果:
INSERT 0 1
最初は、対象レコードがまだテーブルに存在しないので、
無地にinsert できました。
次に、以下のSQLを実行します
INSERT INTO test VALUES(2, 'a', 1, '2020-12-02 13:14:15');
結果
ERROR: unique_violation
CONTEXT: PL/pgSQL 関数 trigger_unique_check_func() の6行目 - RAISE
SQL state: 23505
オー、ちゃんっとエラーでました!
以上です。