0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【小ネタ】PostgreSQLで特定のカラムにユニーク制約を貼りたい!だが、論理削除したデータは除外したい

Last updated at Posted at 2025-05-02

はじめに

例えば以下のようなadministrators(管理者)テーブルがあるとします。
また、usernamepasswordはログインに利用します。
そして管理者の削除は、物理削除ではなく、論理削除で行います。

カラム名 NOT NULL
id integer YES
username varchar(255) YES
password varchar(255) YES
deleted_at timestamp

この場合、usernameはログインIDの役割を果たすのでにユニーク制約を貼ることになるかと思いますが、
以下のように張った場合、問題が発生します。

CREATE UNIQUE INDEX
  administrators_username_unique ON
  administrators (username);

管理者Aとして以下のデータを登録した後に、

カラム名
id 1
username adminA
deleted_at NULL

管理者Bとして以下のデータを登録しようとするとエラーになるには当たり前ですが、

カラム名
id 2
username adminA
deleted_at NULL

管理者Aを以下のように論理削除したとしても、管理者Bの登録はエラーになってしまいます。
これだと、削除の意味がありませんね:cry:

カラム名
id 1
username adminA
deleted_at 2025-05-02 12:00:00

じゃあ、複合ユニークにすればいいのでは?

論理削除データは除外したいんなら、deleted_atカラムも見るように複合カラムでユニークキーを貼ればいいのでは?と思ったりもするかと思います。

CREATE UNIQUE INDEX
  administrators_username_unique ON
  administrators (username, deleted_at);

しかし実は、UNIQUE INDEXは、デフォルトではNULLを区別しないため、今度は同じusernameの管理者データが複数登録できてしまいます。

NULLS NOT DISTINCTの登場

そこで、PostgreSQL15から使えるようになったNULLS NOT DISTINCTの出番です。
以下のように制約を貼ることで、usernameの重複は許さない、かつ、論理削除されたデータは除外ということが出来るのです!!!

CREATE UNIQUE INDEX
  administrators_username_unique ON
  administrators (username, deleted_at) NULLS NOT DISTINCT;

ちなみに、エラーとしてはこんな感じです。

ERROR:  duplicate key value violates unique constraint "administrators_username_unique"
DETAIL:  Key (username, deleted_at)=(adminA, null) already exists.
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?