sqlアンチパターン読書会 第10章 サーティワンフレーバー

30
SQLアンチパターン読書会 第10章 サーティワンフレーバー(31のフレーバー) 2013/10/17 ふじもと たかひさ Twitter:@tkfuji 1

Upload: tkfuji

Post on 27-Jun-2015

1.175 views

Category:

Education


5 download

DESCRIPTION

2013年10月17日開催のSQLアンチパターン読書会 の資料。「第10章 サーティワンフレーバー(31のフレーバー)」。

TRANSCRIPT

Page 1: SQLアンチパターン読書会 第10章 サーティワンフレーバー

SQLアンチパターン読書会第10章 サーティワンフレーバー(31のフレーバー)

2013/10/17ふじもと たかひさ Twitter:@tkfuji

1

Page 2: SQLアンチパターン読書会 第10章 サーティワンフレーバー

アンチパターンの目的サーティワンフレーバーの目的は列を特定の値に限定すること。

値リストを列のデータ型や制約の宣言時に行うことで(値リストにない)無効な文字列の入力を防止できる。

2

Page 3: SQLアンチパターン読書会 第10章 サーティワンフレーバー

敬称列の例値の種類が少ない列、連絡先情報テーブルの敬称(salutation)列が典型。

31-Flavors/intro/create-table.sql

CREATE TABLE PersonalContacts ( -- 他の列. . .

salutation VARCHAR(4) CHECK (salutation IN ('Mr.', 'Mrs.', 'Ms.', 'Dr.', 'Rev.')),);

3

Page 4: SQLアンチパターン読書会 第10章 サーティワンフレーバー

敬称列の例上司からあなたに与えられたタスク:新しい敬称をサポートするために連絡先テーブルを変更せよ

敬称(salutation)列の現在の値 ‘Mr.’, ‘Mrs.’, ‘Ms.’, ‘Dr.’(博士), ‘Rev.’(聖職者)

フランスに支社を開設⇒フランス後の敬称が新たに必要。‘M.’, ‘Mme,’, ‘Mlle.’など

来月ブラジルにも支社を開設予定⇒ポルトガル語も必要?

無停止でテーブル定義が修正できるか?

4

Page 5: SQLアンチパターン読書会 第10章 サーティワンフレーバー

バスキン・ロビンス社のサーティワンアイスクリーム

バスキン・ロビンス社が毎月日替わりで違う種類のアイスクリームが食べられる。キャッチフレーズ「31のフレーバー」

現在、21種類の定番フレーバー、12種類のシーズン限定フレーバー、16種類の地域限定フレーバー、今月のフレーバーと可変になっている。

(何れも『SQLアンチパターン』の記述から)

5

Page 6: SQLアンチパターン読書会 第10章 サーティワンフレーバー

アンチパターンアンチパターン:限定する値を列定義で指定する。

有効なデータ値を列の定義時に指定する方法を取る。例えば、CHECK制約を定義する。CHECK制約により制約条件がFALSEになる値の挿入や更新を拒否する。

31-Flavors/anti/create-table-check.sql

CREATE TABLE Bugs ( -- 他の列. . .

status VARCHAR(20) CHECK (status IN ('NEW', 'IN PROGRESS', 'FIXED')));

6

Page 7: SQLアンチパターン読書会 第10章 サーティワンフレーバー

アンチパターンの実現方法CHECK制約を用いる。

ENUMと呼ばれる非標準のデータ型を用いる(MySQL固有?)。

ドメイン(DOMAIN)を用いる。

ユーザー定義型(UDT)を用いる。

許可する値を指定したトリガーを記述する。

⇒上記には共通の短所がある。

7

Page 8: SQLアンチパターン読書会 第10章 サーティワンフレーバー

アンチパターンの短所シンプルなクエリでは完全なリストを取得できない場合がある。

⇒全てのバグのステータスがNEWの場合、NEWしか返って来ない。

31-Flavors/anti/create-table-check.sql

SELECT DISTINCT status FROM Bugs;

8

Page 9: SQLアンチパターン読書会 第10章 サーティワンフレーバー

アンチパターンの短所メタデータの定義を取得する。MySQLではシステムビューinformation_schemaを検索する。

⇒全てのバグのステータスを含んだ文字列が返って来る。ENUMで定義した文字列「ENUM('NEW', 'IN PROGRESS', 'FIXED')」をアプリケーションで解析して、値を抽出する。

31-Flavors/anti/information-schema.sql

SELECT column_typeFROM information_schema.columnsWHERE table_schema = 'bugtracker_schema' AND table_name = 'bugs' AND column_name = 'status';

9

Page 10: SQLアンチパターン読書会 第10章 サーティワンフレーバー

新しいフレーバーの追加ENUMやCHECK制約で値を追加、削除はできない。

31-Flavors/anti/add-enum-value.sql

ALTER TABLE Bugs MODIFY COLUMN status ENUM('NEW', 'IN PROGRESS', 'FIXED', 'DUPLICATE');

10

Page 11: SQLアンチパターン読書会 第10章 サーティワンフレーバー

アンチパターンは移植が困難データベース製品間で仕様が異なる。CHECK制約

ENUM

ドメイン(DOMAIN)

ユーザー定義型(UDT)

トリガー11

Page 12: SQLアンチパターン読書会 第10章 サーティワンフレーバー

アンチパターンは移植が困難ふじもと調べ

アンチパターンの対応状況(○:対応、×:非対応)ANSI SQL 標準

PostgreSQL MySQL Oracle SQL

ServerDB2 UDB

CHECK制約

ENUM

DOMAIN

UDT

SQL89で標準化 ○ × ○ ○ ○なし ○ ○ × × ×SQL92で標準化 ○ × × × ×SQL99で標準化 ○ × ○ ○ ○

12

Page 13: SQLアンチパターン読書会 第10章 サーティワンフレーバー

CHECK制約の実装状況ふじもと調べ

PostgreSQL特定の列の値がブーリアン(真の値)の式を満たすように指定できる。列制約またはテーブル制約として指定可能。ただし、列制約として定義してもテーブル制約として管理される。

MySQLなし。「CHECK 条項は、全てのストレージ エンジンに解析されますが、無視されます。」( マニュアルから)

Oracle特定の列の値がブーリアン(真の値)の式を満たすように指定できる。列制約またはテーブル制約として指定可能。

SQL Server特定の列の値がブーリアン(真の値)の式を満たすように指定できる。列制約またはテーブル制約として指定可能。

DB2 UDB列制約またはテーブル制約として指定可能。チェック条件は2MBのCLOBとして管理。

13

Page 14: SQLアンチパターン読書会 第10章 サーティワンフレーバー

CHECK制約の実装状況ふじもと調べ

SQL Serverのマニュアルから「列に格納される値を制御するという点では、CHECK 制約は FOREIGN KEY 制約に似ています。異なる点は、有効な値を決定する方法です。FOREIGN KEY 制約では別のテーブルから有効な値の一覧が取得されますが、CHECK 制約では論理式から有効な値が決定されます。」

⇒CHECK制約(アンチパターン)の代替として 参照テーブル(解決策)が提示されている。

14

Page 15: SQLアンチパターン読書会 第10章 サーティワンフレーバー

ENUMの実装状況ふじもと調べ

PostgreSQL8.3からサポート。CREATE TYPEコマンドで実現。列挙型内の値の順序はその型が作成された時に値を列挙した順番になる。

MySQL文字列タイプのひとつ。文字列タイプにはCHAR、VARCHAR、BINARY、VARBINARY、BLOB、TEXT、ENUM、SETがある。ENUMはテーブルを作成する際カラム仕様の中で明確に列挙された許容値リストから選択された値を持つ文字列オブジェクトである。ENUM('one', 'two', 'three') のように1つのENUMは最大65,535エレメントを持つことができる。許容値の文字列または1から始まるインデックス値(定義順)で挿入が可能。許容しない値は特別エラー値として空文字(インデックス値は0)が登録される(ストリクトSQLモードを有効にすることより挿入を禁止することも可能)。NULL値のインデックス値はNULL。

Oracle / SQL Server / DB2 UDBなし。

15

Page 16: SQLアンチパターン読書会 第10章 サーティワンフレーバー

ドメイン(DOMAIN)の実装状況 ふじもと調べ

PostgreSQLCREATE DOMAINコマンドで実現。DEFAULT句、NOT NULL制約、CHECK制約などを定義する。CREATE DOMAIN name [ AS ] data_type

[ COLLATE collation ] [ DEFAULT expression ] [ constraint [ ... ] ]

constraintは、次のようになります。

[ CONSTRAINT constraint_name ] { NOT NULL | NULL | CHECK (expression) }

※ COLLATEは9.1から、CHECKは7.4から

MySQL / Oracle / SQL Server / DB2 UDBなし。

16

Page 17: SQLアンチパターン読書会 第10章 サーティワンフレーバー

ユーザー定義型( UDT)の実装状況 ふじもと調べ

PostgreSQLCREATE TYPEコマンドで実現。定義が面倒。

MySQLなし。

OracleCREATE TYPE文とCREATE TYPE BODY文を使用する。CREATE TYPE BODY文でPL/SQLを使用して定義する。

SQL ServerCREATE TYPEステートメントを使用する。C#やVBの.NETプログラミング言語で、UDT の作成の仕様を満たすクラスまたは構造体を作成する。

DB2 UDBユーザー定義の構造化タイプ。CREATE DISTINCT TYPEステートメントを使用する。既存のデータ・タイプを基に定義する。UDF(ユーザー定義関数)でUDTの振る舞いを定義する。

17

Page 18: SQLアンチパターン読書会 第10章 サーティワンフレーバー

アンチパターンの見つけ方「アプリケーションメニューの選択肢を1つ追加するには、データベースをオフラインにしなければならない。すべてがうまくいけば、30分もかからないはずだ」⇒列の定義時に値が指定されている。サービス中断は本来必要ない。

「status列は、以下の値のうちの1つを持つことができる。このリストの修正が必要になることは、まずないはずだ」⇒曖昧な表現。状況が変われば修正が必要になるかも。

「アプリケーションコード側の値リストが、ビジネスルールと同期していない。これで何度目だろう」⇒異なる場所で情報を二重管理していると生じるリスク。

18

Page 19: SQLアンチパターン読書会 第10章 サーティワンフレーバー

アンチパターンを用いてよい場合有効値の変更が不要だと断言できる場合はアンチパターンを用いてよい。

例えば、相互排他的な2つの値(左/右、有効/無効、オン/オフ、内部/外部)を指定する場合など。

19

Page 20: SQLアンチパターン読書会 第10章 サーティワンフレーバー

解決策:限定する値をデータで指定する

参照するテーブルBugStatusを作成し、許可する値をstatus列に格納する。Bugs.statusにBugStatusを参照する外部キー制約を宣言する。

31-Flavors/soln/create-lookup-table.sql

CREATE TABLE BugStatus ( status VARCHAR(20) PRIMARY KEY);

INSERT INTO BugStatus (status) VALUES ('NEW'), ('IN PROGRESS'), ('FIXED');

CREATE TABLE Bugs ( -- 他の列. . .

status VARCHAR(20), FOREIGN KEY (status) REFERENCES BugStatus(status) ON UPDATE CASCADE);

20

Page 21: SQLアンチパターン読書会 第10章 サーティワンフレーバー

解決策での値セットの取得シンプルなクエリで値リストを取得できる。

⇒全てのバグのステータスがBugsでNEWの場合 でも全ての値が取得可能。

31-Flavors/soln/query-canonical-values.sql

SELECT status FROM BugStatus ORDER BY status;

21

Page 22: SQLアンチパターン読書会 第10章 サーティワンフレーバー

解決策での値追加INSERT文で値を追加できる。テーブルへのアクセス遮断も不要。

31-Flavors/soln/insert-value.sql

INSERT INTO BugStatus (status) VALUES ('DUPLICATE');

22

Page 23: SQLアンチパターン読書会 第10章 サーティワンフレーバー

解決策での値削除Bugsテーブルから参照されている場合、削除できない。BugStatusにactive列(有効/無効)を追加する。

DELETEではなくUPDATEを使う。

有効なものだけ検索する。

31-Flavors/soln/inactive.sql

ALTER TABLE BugStatus ADD COLUMN active ENUM('INACTIVE', 'ACTIVE') NOT NULL DEFAULT 'ACTIVE';

31-Flavors/soln/update-inactive.sql

UPDATE BugStatus SET active = 'INACTIVE' WHERE status = 'DUPLICATE';

31-Flavors/soln/select-active.sql

SELECT status FROM BugStatus WHERE active = 'ACTIVE';

23

Page 24: SQLアンチパターン読書会 第10章 サーティワンフレーバー

31のフレーバーなのか?ふじもと調べ

バスキン・ロビンス社が毎月日替わりで違う種類のアイスクリームが食べられる。キャッチフレーズ「31のフレーバー」

現在、21種類の定番フレーバー、12種類のシーズン限定フレーバー、16種類の地域限定フレーバー、今月のフレーバーと可変になっている。(上記は『SQLアンチパターン』の記述から)⇒(日本)22種類の定番フレーバー、6種類のシーズン限定フレーバー、4種類の今月のフレーバー⇒合計32種類のフレーバー?⇒(アメリカ)24種類の定番フレーバー、28種類のシーズン限定フレーバー、5種類のチョイスフレーバー⇒合計57種類のフレーバー?(それぞれ日本とUSのWebサイトから)

24

Page 25: SQLアンチパターン読書会 第10章 サーティワンフレーバー

31のフレーバーか?サーティワンアイスクリーム行ってみた

南砂町店(江東区)

32種類でした!4×4(写真)×2

25

Page 26: SQLアンチパターン読書会 第10章 サーティワンフレーバー

バスキン・ロビンス社のサーティワンアイスクリームの豆知識 ふじもと調べ(Wikipediaを参考)

創業者がバートン・バスキンとアーヴィン・ロビンス。 バートン・バスキンはアーヴィン・ロビンスの義理の兄(姉の夫)。

アーヴィン・ロビンスの父親がアイスクリーム店を経営し、二人にアイスクリーム店開業を勧める。その後、その二つの店が合併した。

アメリカでは「バスキン・ロビンス(Baskin-Robbins)」で通じるが、「サーティワン(thirty-one)」では通じない(thirty-oneの呼称自体無い)。日本と台湾では「サーティワン」で通じるが、「バスキン・ロビンス」では通じにくい。「サーティーワン」ではなく「サーティワン」。

日本ではアメリカのバスキン・ロビンス社日本法人と不二家(各43.27%の出資比率)の合弁会社が経営。年間売上200億円(平均単価400円として5000万個の売上。1100店として一日当たり125個の売上)。サーティワンアイスクリームが不二家との合弁であることが知られていないため、「不二家の期限切れ原材料使用問題」による影響は比較的小さかった。

フレーバーの種類は1000種類以上。フレーバーは世界どこでも大抵32種類から選べる(高速道路施設などの小規模店は除く)。

26

Page 27: SQLアンチパターン読書会 第10章 サーティワンフレーバー

アンチパターンを31のフレーバーに用いてはいけない理由 ふじもと考察

1000種類以上の開発済みのフレーバーから32種類のフレーバーが販売される。

今月のフレーバーは月単位で入れ替えあり。

シーズン限定フレーバーは季節単位で入れ替えあり。

定番フレーバーすら入れ替えが想定される。

32種類より少ないフレーバーを販売する店もある。

種別(定番・シーズン限定・今月)も管理したいだろう。

27

Page 28: SQLアンチパターン読書会 第10章 サーティワンフレーバー

31のフレーバーは解決策参照テーブルで実現するべき

どうやって実現するかは宿題!

28

Page 29: SQLアンチパターン読書会 第10章 サーティワンフレーバー

31のフレーバーは解決策参照テーブルで実現するべきヒント(≒答え)

販売明細テーブルにフレーバーを示すカラムがある。これをアンチパターン(フレーバーを示すカラムに値リストを設定)を用いないで解決する。

1000種類以上の開発済みのフレーバーを管理するフレーバーテーブル。フレーバー種別カラムを持つ。

地域フレーバーテーブル。地域テーブルとフレーバーテーブルと従属関係にある子テーブル。年月を主キーとして加える(有効開始年月日でもよい)。

店舗フレーバーテーブル。地域フレーバーテーブルと店舗テーブルと従属関係にある子テーブル。年月を主キーとして加える(有効開始年月日でもよい)。

販売明細は地域フレーバーテーブル(通常店の場合)か店舗フレーバーテーブル(小規模店の場合)と参照関係を持つ。

29

Page 30: SQLアンチパターン読書会 第10章 サーティワンフレーバー

おまけ:SQLデモ本のサンプルコードを基に ENUMとCHECK制約のアンチパターンのSQL作成。

本のサンプルコードは実行可能でないものも含まれるため。http://www.oreilly.co.jp/books/9784873115894/

PostgreSQLで実行。MySQLは(サンプルコードにある)CHECK制約が実行できないため。

30