44
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

論理削除とユニークキー制約の両立

Last updated at Posted at 2015-11-12

概要

データベーステーブルの物理設計を行うとき、論理削除とユニークキー制約を両立させるための TIPS です。

基本形

create table user (
    id int not null,
    email varchar(40) not null,
    password_sha256 varchar(64) not null,
    removed int not null default 0, -- 論理削除カラム
    constraint pk primary key (id)
);

メアドとパスワードでログインできる user テーブルを例とします。
removed = 0 が有効なレコードです。
退会時には removed = 1 に更新します。

-- 入会
insert into user (id, email, password_sha256)
  values (1, 'foo@example.com', '**dummy**');

-- ログイン removed = 0
select * from user where removed = 0 and email = 'foo@example.com';

-- 退会 removed = 1 で更新
update user set removed = 1 where id = 1;

email にユニークキー制約を付ける

drop table user;
create table user (
    id int not null,
    email varchar(40) not null,
    password_sha256 varchar(64) not null,
    removed int not null default 0, -- 論理削除カラム
    constraint pk primary key (id),
    constraint uk_email unique key (email) -- ユニークキー制約
);

email にユニークキー制約をつけてしまうと、一度退会した会員が同じメアドで再度入会することができなくなってしまいます。

-- 入会
insert into user (id, email, password_sha256)
  values (1, 'foo@example.com', '**dummy**');

-- ログイン removed = 0
select * from user where removed = 0 and email = 'foo@example.com';

-- 退会 removed = 1 で更新
update user set removed = 1 where id = 1;

-- 再入会
insert into user (id, email, password_sha256)
  values (2, 'foo@example.com', '**dummy**'); -- 重複エラー!

そこで

drop table user;
create table user (
    id int not null,
    email varchar(40) not null,
    password_sha256 varchar(64) not null,
    removed int not null default 0, -- 論理削除カラム
    constraint pk primary key (id),
    constraint uk_email unique key (email, removed) -- removed を含めたユニークキー制約
);

email と removed の複合ユニークキー制約にします。
退会時には removed = 1 ではなく、removed = id で更新するのがキモです。

-- 入会
insert into user (id, email, password_sha256)
  values (1, 'foo@example.com', '**dummy**');

-- ログイン removed = 0
select * from user where removed = 0 and email = 'foo@example.com';

-- 退会 removed = id で更新
update user set removed = id where id = 1;

-- 再入会
insert into user (id, email, password_sha256)
  values (2, 'foo@example.com', '**dummy**'); -- 成功

この仕組みなら何度でも同じメアドで入退会を繰り返すことが可能です。

+----+-----------------+-----------------+---------+
| id | email           | password_sha256 | removed |
+----+-----------------+-----------------+---------+
|  1 | foo@example.com | **dummy**       |       1 |
|  2 | foo@example.com | **dummy**       |       2 |
|  3 | foo@example.com | **dummy**       |       0 | ← このレコードのみが有効
+----+-----------------+-----------------+---------+

データベース

例で使用した sql は mysql でも postgresql でも動作するようにしましたが、mysql なら id は auto_increment を使うと良いでしょう。

なおこの記事は http://blog.utils.jp/2009/07/blog-post.html の書き直しとなります。

44
28
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
44
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?