技術

『達人に学ぶSQL徹底指南書 第2版』の環境構築

SQLを勉強し直す必要に迫られたので、『達人に学ぶSQL徹底指南書 第2版』を手を動かしながら勉強できる環境をローカルに構築しました。

本記事は、その環境構築についてになります。

前置き

なんでSQLの勉強が必要になったか

とある案件にデータエンジニアとしてジョインすることになりました。

私のこれまでデータエンジニアとしての業務経験がないので、データ基盤系の知識はもとより、SQLそのものもあまり詳しくはありません。バックエンドで求められるレベルの簡単なSQLならわかりますが、データエンジニアに求められる少し複雑なSQLを書けるスキルは現状、持ち合わせていません。

なのでジョインまでに、少しSQLのスキルを底上げしとこうと思いました。

達人に学ぶSQL徹底指南書 第2版 について

これはSQL中級レベルの勉強本です。

中級レベルですので、SQLの基本構文の記載はありません。また昔ながらのRDBを想定して書かれているので、最近のデータ基盤に特化した書き方などはありません。

中級レベルとなっていますが、個人的にはかなり難しいと思います。本書のSQLが書けるなら、業務ではSQL関連で困ることはないかと思います。

本書はSQL関連の例題が豊富で、実際にSQLを書きながら勉強できる構成になっています。

しかし、そのSQLの実行環境そのものは自分で用意する必要があります。個人的にはDockerイメージとか用意してくれれてらいいのにと思いましたが、初版はDockerそのものが登場する前に書かれたものですし、2,500円の書籍にそこまで求めるものでもないですね。

というわけで、以下はこのSQL実行環境を構築したときの備忘録になります。

達人に学ぶSQL徹底指南書の環境構築

方針

方針は以下の様にしました。

  • DBMSはpostgres
  • 公式Dockerイメージを使う
  • データ部は永続化する

本書は実装依存(特定のDBMSのみで動作する)のSQLはないので、DBMSはなんでもいいです。今回はPostgreSQLを使うことにしました。

ローカルに直接インストールするのは避けて、Dockerを使うことにしました。Dockerコンテナは柔軟に消したり作ったりしたいので、データ部は永続化することにします。

以下、手順になります。

手順

コンテナ起動まで

まず、postgresの公式イメージをDLします。

docker pull postgres

カレンディレクトリに、データ永続に使用するディレクトリを作成します。今回はdataという名前で作成しました。

mkdir data

DLしたイメージで、コンテナを起動します。

docker run -d --name study_db \
-p 5432:5432 \
-v ~/data:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=postgres \
postgres

POSTGRES_PASSWORDは設定しないと、コンテナは起動しませんでした。

以上で、完了です。

docker psでコンテナが起動していることを確認して、ローカルから接続します。

サンプルデータ投入

本書で使用されるサンプルデータ(テーブルデータ)は、以下で用意してくれています。

http://mickindex.sakura.ne.jp/database/db_support_sinan.html

ただ、正直言って少し不親切かと思います。

サンプルデータのCREATE文は1章ずつ細切れになっています。コピペがかなり大変です。これは、全スキーマDDLみたいのを用意して、1回でできるようにしてくれと思いました。CREATEもIF NOT EXISTSがなく、再作成時には手動でDROPする必要があります。

しかし、これは作業していて納得しました。

私は、最初に全サンプルデータをコピペして、一気に作ろうとしたのですが、エラーが大量に出て、できませんでした。

どうやら、ところどころで同じテーブル名を使いまわしているようです。テーブル名は同じですが、カラム制約が微妙に違ったりするので、IF NOT EXISTSはつけられないし、章ごとにテーブルをDROPしたり作成したりを前提としているようです。

これも、どうかと思いました。テーブル名は分けて、各章のサンプルデータは残るようにして欲しかったです。

すでに全部コピペしたDDLファイルを作成してしまったので、エラーが出た箇所をひたすらコメントアウトして、一応必要なテーブルは全部作成しました。ただ前述の通り、各章でカラム制約が違ったりするみたいなので、毎回確認する必要がありそうです。

とはいえ、これでSQLの勉強環境が整いました。あとはひたすら、本書を読んで例題をやっていくだけです。


最後にコメントアウトしまくった全テーブル作成DDLを載せておきます。あまり役には立たないとは思いますが・・・

/* 既存のコード体系を新しい体系に変換して集計 */
CREATE TABLE PopTbl
(pref_name VARCHAR(32) PRIMARY KEY,
 population INTEGER NOT NULL);

INSERT INTO PopTbl VALUES('徳島', 100);
INSERT INTO PopTbl VALUES('香川', 200);
INSERT INTO PopTbl VALUES('愛媛', 150);
INSERT INTO PopTbl VALUES('高知', 200);
INSERT INTO PopTbl VALUES('福岡', 300);
INSERT INTO PopTbl VALUES('佐賀', 100);
INSERT INTO PopTbl VALUES('長崎', 200);
INSERT INTO PopTbl VALUES('東京', 400);
INSERT INTO PopTbl VALUES('群馬', 50);


/* 異なる条件の集計を1つのSQLで行う */
CREATE TABLE PopTbl2
(pref_name VARCHAR(32),
 sex CHAR(1) NOT NULL,
 population INTEGER NOT NULL,
    PRIMARY KEY(pref_name, sex));

INSERT INTO PopTbl2 VALUES('徳島', '1',   60 );
INSERT INTO PopTbl2 VALUES('徳島', '2',   40 );
INSERT INTO PopTbl2 VALUES('香川', '1',   100);
INSERT INTO PopTbl2 VALUES('香川', '2',   100);
INSERT INTO PopTbl2 VALUES('愛媛', '1',   100);
INSERT INTO PopTbl2 VALUES('愛媛', '2',   50 );
INSERT INTO PopTbl2 VALUES('高知', '1',   100);
INSERT INTO PopTbl2 VALUES('高知', '2',   100);
INSERT INTO PopTbl2 VALUES('福岡', '1',   100);
INSERT INTO PopTbl2 VALUES('福岡', '2',   200);
INSERT INTO PopTbl2 VALUES('佐賀', '1',   20 );
INSERT INTO PopTbl2 VALUES('佐賀', '2',   80 );
INSERT INTO PopTbl2 VALUES('長崎', '1',   125);
INSERT INTO PopTbl2 VALUES('長崎', '2',   125);
INSERT INTO PopTbl2 VALUES('東京', '1',   250);
INSERT INTO PopTbl2 VALUES('東京', '2',   150);


/* CHECK制約で複数の列の条件関係を定義する */
CREATE TABLE TestSal
(sex CHAR(1) ,
 salary INTEGER,
    CONSTRAINT check_salary CHECK
             ( CASE WHEN sex = '2'
                    THEN CASE WHEN salary <= 200000
                              THEN 1 ELSE 0 END
                    ELSE 1 END = 1 ));

INSERT INTO TestSal VALUES(1, 200000);
INSERT INTO TestSal VALUES(1, 300000);
INSERT INTO TestSal VALUES(1, NULL);
INSERT INTO TestSal VALUES(2, 200000);
-- INSERT INTO TestSal VALUES(2, 300000);  --error
-- INSERT INTO TestSal VALUES(2, NULL);
INSERT INTO TestSal VALUES(1, 300000);


/* 条件を分岐させたUPDATE */
CREATE TABLE SomeTable
(p_key CHAR(1) PRIMARY KEY,
 col_1 INTEGER NOT NULL,
 col_2 CHAR(2) NOT NULL);

INSERT INTO SomeTable VALUES('a', 1, 'あ');
INSERT INTO SomeTable VALUES('b', 2, 'い');
INSERT INTO SomeTable VALUES('c', 3, 'う');


/* テーブル同士のマッチング */
CREATE TABLE CourseMaster
(course_id   INTEGER PRIMARY KEY,
 course_name VARCHAR(32) NOT NULL);

INSERT INTO CourseMaster VALUES(1, '経理入門');
INSERT INTO CourseMaster VALUES(2, '財務知識');
INSERT INTO CourseMaster VALUES(3, '簿記検定');
INSERT INTO CourseMaster VALUES(4, '税理士');

CREATE TABLE OpenCourses
(month       INTEGER ,
 course_id   INTEGER ,
    PRIMARY KEY(month, course_id));

INSERT INTO OpenCourses VALUES(200706, 1);
INSERT INTO OpenCourses VALUES(200706, 3);
INSERT INTO OpenCourses VALUES(200706, 4);
INSERT INTO OpenCourses VALUES(200707, 4);
INSERT INTO OpenCourses VALUES(200708, 2);
INSERT INTO OpenCourses VALUES(200708, 4);


/* CASE式の中で集約関数を使う */
CREATE TABLE StudentClub
(std_id  INTEGER,
 club_id INTEGER,
 club_name VARCHAR(32),
 main_club_flg CHAR(1),
 PRIMARY KEY (std_id, club_id));

INSERT INTO StudentClub VALUES(100, 1, '野球',        'Y');
INSERT INTO StudentClub VALUES(100, 2, '吹奏楽',      'N');
INSERT INTO StudentClub VALUES(200, 2, '吹奏楽',      'N');
INSERT INTO StudentClub VALUES(200, 3, 'バドミントン','Y');
INSERT INTO StudentClub VALUES(200, 4, 'サッカー',    'N');
INSERT INTO StudentClub VALUES(300, 4, 'サッカー',    'N');
INSERT INTO StudentClub VALUES(400, 5, '水泳',        'N');
INSERT INTO StudentClub VALUES(500, 6, '囲碁',        'N');

CREATE TABLE Products
(name VARCHAR(16) PRIMARY KEY,
 price INTEGER NOT NULL);

--重複順列・順列・組み合わせ
INSERT INTO Products VALUES('りんご',  100);
INSERT INTO Products VALUES('みかん',  50);
INSERT INTO Products VALUES('バナナ',  80);

--部分的に不一致なキーの検索
CREATE TABLE Addresses
(name VARCHAR(32),
 family_id INTEGER,
 address VARCHAR(32),
 PRIMARY KEY(name, family_id));

INSERT INTO Addresses VALUES('前田 義明', '100', '東京都港区虎ノ門3-2-29');
INSERT INTO Addresses VALUES('前田 由美', '100', '東京都港区虎ノ門3-2-92');
INSERT INTO Addresses VALUES('加藤 茶',   '200', '東京都新宿区西新宿2-8-1');
INSERT INTO Addresses VALUES('加藤 勝',   '200', '東京都新宿区西新宿2-8-1');
INSERT INTO Addresses VALUES('ホームズ',  '300', 'ベーカー街221B');
INSERT INTO Addresses VALUES('ワトソン',  '400', 'ベーカー街221B');

--ランキング
-- DELETE FROM Products;
-- INSERT INTO Products VALUES('りんご',   50);
-- INSERT INTO Products VALUES('みかん',   100);
-- INSERT INTO Products VALUES('ぶどう',   50);
-- INSERT INTO Products VALUES('スイカ',   80);
-- INSERT INTO Products VALUES('レモン',   30);
-- INSERT INTO Products VALUES('バナナ',   50);
--
-- --非集約テスト用
-- DELETE FROM Products;
-- INSERT INTO Products VALUES('みかん',   100);
-- INSERT INTO Products VALUES('ぶどう',   50);
-- INSERT INTO Products VALUES('スイカ',   80);
-- INSERT INTO Products VALUES('レモン',   30);

/* 実践編:1. 比較述語とNULL その1:排中律が成立しない */
CREATE TABLE Students
(name VARCHAR(16) PRIMARY KEY,
 age  INTEGER );

INSERT INTO Students VALUES('ブラウン', 22);
INSERT INTO Students VALUES('ラリー',   19);
INSERT INTO Students VALUES('ジョン',   NULL);
INSERT INTO Students VALUES('ボギー', 21);


/* 実践編:3. NOT IN とNOT EXISTS は同値ではない */
CREATE TABLE Class_A
(name VARCHAR(16) PRIMARY KEY,
 age  INTEGER,
 city VARCHAR(16) NOT NULL );

CREATE TABLE Class_B
(name VARCHAR(16) PRIMARY KEY,
 age  INTEGER,
 city VARCHAR(16) NOT NULL );

INSERT INTO Class_A VALUES('ブラウン', 22, '東京');
INSERT INTO Class_A VALUES('ラリー',   19, '埼玉');
INSERT INTO Class_A VALUES('ボギー',   21, '千葉');

INSERT INTO Class_B VALUES('斎藤',  22,   '東京');
INSERT INTO Class_B VALUES('田尻',  23,   '東京');
INSERT INTO Class_B VALUES('山田',  NULL, '東京');
INSERT INTO Class_B VALUES('和泉',  18,   '千葉');
INSERT INTO Class_B VALUES('武田',  20,   '千葉');
INSERT INTO Class_B VALUES('石川',  19,   '神奈川');


/* 実践編:4.限定述語とNULL */
-- DELETE FROM Class_A;
-- INSERT INTO Class_A VALUES('ブラウン', 22, '東京');
-- INSERT INTO Class_A VALUES('ラリー',   19, '埼玉');
-- INSERT INTO Class_A VALUES('ボギー',   21, '千葉');
--
-- DELETE FROM Class_B;
-- INSERT INTO Class_B VALUES('斎藤', 22, '東京');
-- INSERT INTO Class_B VALUES('田尻', 23, '東京');
-- INSERT INTO Class_B VALUES('山田', 20, '東京');
-- INSERT INTO Class_B VALUES('和泉', 18, '千葉');
-- INSERT INTO Class_B VALUES('武田', 20, '千葉');
-- INSERT INTO Class_B VALUES('石川', 19, '神奈川');
--
--
-- /* 実践編:5.限定述語と極値関数は同値ではない */
-- DELETE FROM Class_B;
-- INSERT INTO Class_B VALUES('和泉', 18, '千葉');
-- INSERT INTO Class_B VALUES('武田', 20, '千葉');
-- INSERT INTO Class_B VALUES('石川', 19, '神奈川');

/* データの歯抜けを探す */
CREATE TABLE SeqTbl
(seq  INTEGER PRIMARY KEY,
 name VARCHAR(16) NOT NULL);

INSERT INTO SeqTbl VALUES(1,    'ディック');
INSERT INTO SeqTbl VALUES(2,    'アン');
INSERT INTO SeqTbl VALUES(3,    'ライル');
INSERT INTO SeqTbl VALUES(5,    'カー');
INSERT INTO SeqTbl VALUES(6,    'マリー');
INSERT INTO SeqTbl VALUES(8,    'ベン');


/* HAVING 句でサブクエリ:最頻値を求める(メジアンも同じサンプルを使用) */
CREATE TABLE Graduates
(name   VARCHAR(16) PRIMARY KEY,
 income INTEGER NOT NULL);

INSERT INTO Graduates VALUES('サンプソン', 400000);
INSERT INTO Graduates VALUES('マイク',     30000);
INSERT INTO Graduates VALUES('ホワイト',   20000);
INSERT INTO Graduates VALUES('アーノルド', 20000);
INSERT INTO Graduates VALUES('スミス',     20000);
INSERT INTO Graduates VALUES('ロレンス',   15000);
INSERT INTO Graduates VALUES('ハドソン',   15000);
INSERT INTO Graduates VALUES('ケント',     10000);
INSERT INTO Graduates VALUES('ベッカー',   10000);
INSERT INTO Graduates VALUES('スコット',   10000);


-- /* NULL を含まない集合を探す */
-- CREATE TABLE Students
-- (student_id   INTEGER PRIMARY KEY,
--  dpt          VARCHAR(16) NOT NULL,
--  sbmt_date    DATE);
--
-- INSERT INTO Students VALUES(100,  '理学部',   '2005-10-10');
-- INSERT INTO Students VALUES(101,  '理学部',   '2005-09-22');
-- INSERT INTO Students VALUES(102,  '文学部',   NULL);
-- INSERT INTO Students VALUES(103,  '文学部',   '2005-09-10');
-- INSERT INTO Students VALUES(200,  '文学部',   '2005-09-22');
-- INSERT INTO Students VALUES(201,  '工学部',   NULL);
-- INSERT INTO Students VALUES(202,  '経済学部', '2005-09-25');


/* 関係除算でバスケット解析 */
CREATE TABLE Items
(item VARCHAR(16) PRIMARY KEY);

CREATE TABLE ShopItems
(shop VARCHAR(16),
 item VARCHAR(16),
    PRIMARY KEY(shop, item));

INSERT INTO Items VALUES('ビール');
INSERT INTO Items VALUES('紙オムツ');
INSERT INTO Items VALUES('自転車');

INSERT INTO ShopItems VALUES('仙台',  'ビール');
INSERT INTO ShopItems VALUES('仙台',  '紙オムツ');
INSERT INTO ShopItems VALUES('仙台',  '自転車');
INSERT INTO ShopItems VALUES('仙台',  'カーテン');
INSERT INTO ShopItems VALUES('東京',  'ビール');
INSERT INTO ShopItems VALUES('東京',  '紙オムツ');
INSERT INTO ShopItems VALUES('東京',  '自転車');
INSERT INTO ShopItems VALUES('大阪',  'テレビ');
INSERT INTO ShopItems VALUES('大阪',  '紙オムツ');
INSERT INTO ShopItems VALUES('大阪',  '自転車');

/* 外部結合で行列変換 その1(行→列):クロス表を作る */
CREATE TABLE Courses
(name   VARCHAR(32),
 course VARCHAR(32),
 PRIMARY KEY(name, course));

INSERT INTO Courses VALUES('赤井', 'SQL入門');
INSERT INTO Courses VALUES('赤井', 'UNIX基礎');
INSERT INTO Courses VALUES('鈴木', 'SQL入門');
INSERT INTO Courses VALUES('工藤', 'SQL入門');
INSERT INTO Courses VALUES('工藤', 'Java中級');
INSERT INTO Courses VALUES('吉田', 'UNIX基礎');
INSERT INTO Courses VALUES('渡辺', 'SQL入門');

/* 外部結合で行列変換 その2(列→行):繰り返し項目を1 列にまとめる */
CREATE TABLE Personnel
 (employee   varchar(32),
  child_1    varchar(32),
  child_2    varchar(32),
  child_3    varchar(32),
  PRIMARY KEY(employee));

INSERT INTO Personnel VALUES('赤井', '一郎', '二郎', '三郎');
INSERT INTO Personnel VALUES('工藤', '春子', '夏子', NULL);
INSERT INTO Personnel VALUES('鈴木', '夏子', NULL,   NULL);
INSERT INTO Personnel VALUES('吉田', NULL,   NULL,   NULL);

/* クロス表で入れ子の表側を作る */
CREATE TABLE TblSex
(sex_cd   char(1),
 sex varchar(5),
 PRIMARY KEY(sex_cd));

CREATE TABLE TblAge
(age_class char(1),
 age_range varchar(30),
 PRIMARY KEY(age_class));

CREATE TABLE TblPop
(pref_name  varchar(30),
 age_class  char(1),
 sex_cd     char(1),
 population integer,
 PRIMARY KEY(pref_name, age_class,sex_cd));

INSERT INTO TblSex (sex_cd, sex ) VALUES('m',   '男');
INSERT INTO TblSex (sex_cd, sex ) VALUES('f',   '女');

INSERT INTO TblAge (age_class, age_range ) VALUES('1',  '21~30歳');
INSERT INTO TblAge (age_class, age_range ) VALUES('2',  '31~40歳');
INSERT INTO TblAge (age_class, age_range ) VALUES('3',  '41~50歳');

INSERT INTO TblPop VALUES('秋田', '1', 'm', 400 );
INSERT INTO TblPop VALUES('秋田', '3', 'm', 1000 );
INSERT INTO TblPop VALUES('秋田', '1', 'f', 800 );
INSERT INTO TblPop VALUES('秋田', '3', 'f', 1000 );
INSERT INTO TblPop VALUES('青森', '1', 'm', 700 );
INSERT INTO TblPop VALUES('青森', '1', 'f', 500 );
INSERT INTO TblPop VALUES('青森', '3', 'f', 800 );
INSERT INTO TblPop VALUES('東京', '1', 'm', 900 );
INSERT INTO TblPop VALUES('東京', '1', 'f', 1500 );
INSERT INTO TblPop VALUES('東京', '3', 'f', 1200 );
INSERT INTO TblPop VALUES('千葉', '1', 'm', 900 );
INSERT INTO TblPop VALUES('千葉', '1', 'f', 1000 );
INSERT INTO TblPop VALUES('千葉', '3', 'f', 900 );

-- /* 掛け算としての結合 */
-- CREATE TABLE Items
--  (item_no INTEGER PRIMARY KEY,
--   item    VARCHAR(32) NOT NULL);
--
-- INSERT INTO Items VALUES(10, 'FD');
-- INSERT INTO Items VALUES(20, 'CD-R');
-- INSERT INTO Items VALUES(30, 'MO');
-- INSERT INTO Items VALUES(40, 'DVD');

CREATE TABLE SalesHistory
 (sale_date DATE NOT NULL,
  item_no   INTEGER NOT NULL,
  quantity  INTEGER NOT NULL,
  PRIMARY KEY(sale_date, item_no));

INSERT INTO SalesHistory VALUES('2007-10-01',  10,  4);
INSERT INTO SalesHistory VALUES('2007-10-01',  20, 10);
INSERT INTO SalesHistory VALUES('2007-10-01',  30,  3);
INSERT INTO SalesHistory VALUES('2007-10-03',  10, 32);
INSERT INTO SalesHistory VALUES('2007-10-03',  30, 12);
INSERT INTO SalesHistory VALUES('2007-10-04',  20, 22);
INSERT INTO SalesHistory VALUES('2007-10-04',  30,  7);

-- /* 完全外部結合 */
-- CREATE TABLE Class_A
-- (id char(1),
--  name varchar(30),
--  PRIMARY KEY(id));
--
-- CREATE TABLE Class_B
-- (id   char(1),
--  name varchar(30),
--  PRIMARY KEY(id));
--
-- INSERT INTO Class_A (id, name) VALUES('1', '田中');
-- INSERT INTO Class_A (id, name) VALUES('2', '鈴木');
-- INSERT INTO Class_A (id, name) VALUES('3', '伊集院');
--
-- INSERT INTO Class_B (id, name) VALUES('1', '田中');
-- INSERT INTO Class_B (id, name) VALUES('2', '鈴木');
-- INSERT INTO Class_B (id, name) VALUES('4', '西園寺');

--成長・後退・現状維持
CREATE TABLE Sales
(year INTEGER NOT NULL ,
 sale INTEGER NOT NULL ,
 PRIMARY KEY (year));

INSERT INTO Sales VALUES (1990, 50);
INSERT INTO Sales VALUES (1991, 51);
INSERT INTO Sales VALUES (1992, 52);
INSERT INTO Sales VALUES (1993, 52);
INSERT INTO Sales VALUES (1994, 50);
INSERT INTO Sales VALUES (1995, 50);
INSERT INTO Sales VALUES (1996, 49);
INSERT INTO Sales VALUES (1997, 55);

--時系列に歯抜けがある場合:直近と比較
CREATE TABLE Sales2
(year INTEGER NOT NULL ,
 sale INTEGER NOT NULL ,
 PRIMARY KEY (year));

INSERT INTO Sales2 VALUES (1990, 50);
INSERT INTO Sales2 VALUES (1992, 50);
INSERT INTO Sales2 VALUES (1993, 52);
INSERT INTO Sales2 VALUES (1994, 55);
INSERT INTO Sales2 VALUES (1997, 55);

--移動累計と移動平均
CREATE TABLE Accounts
(prc_date DATE NOT NULL ,
 prc_amt  INTEGER NOT NULL ,
 PRIMARY KEY (prc_date)) ;

INSERT INTO Accounts VALUES ('2006-10-26',  12000 );
INSERT INTO Accounts VALUES ('2006-10-28',   2500 );
INSERT INTO Accounts VALUES ('2006-10-31', -15000 );
INSERT INTO Accounts VALUES ('2006-11-03',  34000 );
INSERT INTO Accounts VALUES ('2006-11-04',  -5000 );
INSERT INTO Accounts VALUES ('2006-11-06',   7200 );
INSERT INTO Accounts VALUES ('2006-11-11',  11000 );

--オーバーラップする期間を調べる
CREATE TABLE Reservations
(reserver    VARCHAR(30) PRIMARY KEY,
 start_date  DATE  NOT NULL,
 end_date    DATE  NOT NULL);

INSERT INTO Reservations VALUES('木村', '2006-10-26', '2006-10-27');
INSERT INTO Reservations VALUES('荒木', '2006-10-28', '2006-10-31');
INSERT INTO Reservations VALUES('堀',   '2006-10-31', '2006-11-01');
INSERT INTO Reservations VALUES('山本', '2006-11-03', '2006-11-04');
INSERT INTO Reservations VALUES('内田', '2006-11-03', '2006-11-05');
INSERT INTO Reservations VALUES('水谷', '2006-11-06', '2006-11-06');

--山本氏の投宿日が4日の場合
-- DELETE FROM Reservations WHERE reserver = '山本';
-- INSERT INTO Reservations VALUES('山本', '2006-11-04', '2006-11-04');

/* テーブル同士のコンペア 集合の相等性チェック */
CREATE TABLE Tbl_A
 (keycol  CHAR(1) PRIMARY KEY,
  col_1   INTEGER ,
  col_2   INTEGER,
  col_3   INTEGER);

CREATE TABLE Tbl_B
 (keycol  CHAR(1) PRIMARY KEY,
  col_1   INTEGER,
  col_2   INTEGER,
  col_3   INTEGER);

-- /* 等しいテーブル同士のケース */
-- DELETE FROM Tbl_A;
-- INSERT INTO Tbl_A VALUES('A', 2, 3, 4);
-- INSERT INTO Tbl_A VALUES('B', 0, 7, 9);
-- INSERT INTO Tbl_A VALUES('C', 5, 1, 6);
--
-- DELETE FROM Tbl_B;
-- INSERT INTO Tbl_B VALUES('A', 2, 3, 4);
-- INSERT INTO Tbl_B VALUES('B', 0, 7, 9);
-- INSERT INTO Tbl_B VALUES('C', 5, 1, 6);
--
--
-- /* 「B」の行が相違するケース */
-- DELETE FROM Tbl_A;
-- INSERT INTO Tbl_A VALUES('A', 2, 3, 4);
-- INSERT INTO Tbl_A VALUES('B', 0, 7, 9);
-- INSERT INTO Tbl_A VALUES('C', 5, 1, 6);
--
-- DELETE FROM Tbl_B;
-- INSERT INTO Tbl_B VALUES('A', 2, 3, 4);
-- INSERT INTO Tbl_B VALUES('B', 0, 7, 8);
-- INSERT INTO Tbl_B VALUES('C', 5, 1, 6);
--
--
-- /* NULLを含むケース(等しい) */
-- DELETE FROM Tbl_A;
-- INSERT INTO Tbl_A VALUES('A', NULL, 3, 4);
-- INSERT INTO Tbl_A VALUES('B', 0, 7, 9);
-- INSERT INTO Tbl_A VALUES('C', NULL, NULL, NULL);
--
-- DELETE FROM Tbl_B;
-- INSERT INTO Tbl_B VALUES('A', NULL, 3, 4);
-- INSERT INTO Tbl_B VALUES('B', 0, 7, 9);
-- INSERT INTO Tbl_B VALUES('C', NULL, NULL, NULL);
--
--
-- /* NULLを含むケース(「C」の行が異なる) */
-- DELETE FROM Tbl_A;
-- INSERT INTO Tbl_A VALUES('A', NULL, 3, 4);
-- INSERT INTO Tbl_A VALUES('B', 0, 7, 9);
-- INSERT INTO Tbl_A VALUES('C', NULL, NULL, NULL);
--
-- DELETE FROM Tbl_B;
-- INSERT INTO Tbl_B VALUES('A', NULL, 3, 4);
-- INSERT INTO Tbl_B VALUES('B', 0, 7, 9);
-- INSERT INTO Tbl_B VALUES('C', 0, NULL, NULL);


/* 3. 差集合で関係除算を表現する */
CREATE TABLE Skills
(skill VARCHAR(32),
 PRIMARY KEY(skill));

CREATE TABLE EmpSkills
(emp   VARCHAR(32),
 skill VARCHAR(32),
 PRIMARY KEY(emp, skill));

INSERT INTO Skills VALUES('Oracle');
INSERT INTO Skills VALUES('UNIX');
INSERT INTO Skills VALUES('Java');

INSERT INTO EmpSkills VALUES('相田', 'Oracle');
INSERT INTO EmpSkills VALUES('相田', 'UNIX');
INSERT INTO EmpSkills VALUES('相田', 'Java');
INSERT INTO EmpSkills VALUES('相田', 'C#');
INSERT INTO EmpSkills VALUES('神崎', 'Oracle');
INSERT INTO EmpSkills VALUES('神崎', 'UNIX');
INSERT INTO EmpSkills VALUES('神崎', 'Java');
INSERT INTO EmpSkills VALUES('平井', 'UNIX');
INSERT INTO EmpSkills VALUES('平井', 'Oracle');
INSERT INTO EmpSkills VALUES('平井', 'PHP');
INSERT INTO EmpSkills VALUES('平井', 'Perl');
INSERT INTO EmpSkills VALUES('平井', 'C++');
INSERT INTO EmpSkills VALUES('若田部', 'Perl');
INSERT INTO EmpSkills VALUES('渡来', 'Oracle');

/* 4. 等しい部分集合を見つける */
CREATE TABLE SupParts
(sup  CHAR(32) NOT NULL,
 part CHAR(32) NOT NULL,
 PRIMARY KEY(sup, part));

INSERT INTO SupParts VALUES('A',  'ボルト');
INSERT INTO SupParts VALUES('A',  'ナット');
INSERT INTO SupParts VALUES('A',  'パイプ');
INSERT INTO SupParts VALUES('B',  'ボルト');
INSERT INTO SupParts VALUES('B',  'パイプ');
INSERT INTO SupParts VALUES('C',  'ボルト');
INSERT INTO SupParts VALUES('C',  'ナット');
INSERT INTO SupParts VALUES('C',  'パイプ');
INSERT INTO SupParts VALUES('D',  'ボルト');
INSERT INTO SupParts VALUES('D',  'パイプ');
INSERT INTO SupParts VALUES('E',  'ヒューズ');
INSERT INTO SupParts VALUES('E',  'ナット');
INSERT INTO SupParts VALUES('E',  'パイプ');
INSERT INTO SupParts VALUES('F',  'ヒューズ');

/* 5. 重複行を削除する高速なクエリ
PostgreSQLでは「with oids」をCREATE TABLE文の最後に追加すること */
-- CREATE TABLE Products
-- (name  CHAR(16),
--  price INTEGER);
--
-- INSERT INTO Products VALUES('りんご',  50);
-- INSERT INTO Products VALUES('みかん', 100);
-- INSERT INTO Products VALUES('みかん', 100);
-- INSERT INTO Products VALUES('みかん', 100);
-- INSERT INTO Products VALUES('バナナ',  80);

/* テーブルに存在「しない」データを探す */
CREATE TABLE Meetings
(meeting CHAR(32) NOT NULL,
 person  CHAR(32) NOT NULL,
 PRIMARY KEY (meeting, person));

INSERT INTO Meetings VALUES('第1回', '伊藤');
INSERT INTO Meetings VALUES('第1回', '水島');
INSERT INTO Meetings VALUES('第1回', '坂東');
INSERT INTO Meetings VALUES('第2回', '伊藤');
INSERT INTO Meetings VALUES('第2回', '宮田');
INSERT INTO Meetings VALUES('第3回', '坂東');
INSERT INTO Meetings VALUES('第3回', '水島');
INSERT INTO Meetings VALUES('第3回', '宮田');

/* 全称量化 その1:肯定⇔二重否定の変換に慣れよう */
CREATE TABLE TestScores
(student_id INTEGER,
 subject    VARCHAR(32) ,
 score      INTEGER,
  PRIMARY KEY(student_id, subject));

INSERT INTO TestScores VALUES(100, '算数',100);
INSERT INTO TestScores VALUES(100, '国語',80);
INSERT INTO TestScores VALUES(100, '理科',80);
INSERT INTO TestScores VALUES(200, '算数',80);
INSERT INTO TestScores VALUES(200, '国語',95);
INSERT INTO TestScores VALUES(300, '算数',40);
INSERT INTO TestScores VALUES(300, '国語',90);
INSERT INTO TestScores VALUES(300, '社会',55);
INSERT INTO TestScores VALUES(400, '算数',80);

/* 全称量化 その2:集合VS 述語――凄いのはどっちだ? */
CREATE TABLE Projects
(project_id VARCHAR(32),
 step_nbr   INTEGER ,
 status     VARCHAR(32),
  PRIMARY KEY(project_id, step_nbr));

INSERT INTO Projects VALUES('AA100', 0, '完了');
INSERT INTO Projects VALUES('AA100', 1, '待機');
INSERT INTO Projects VALUES('AA100', 2, '待機');
INSERT INTO Projects VALUES('B200',  0, '待機');
INSERT INTO Projects VALUES('B200',  1, '待機');
INSERT INTO Projects VALUES('CS300', 0, '完了');
INSERT INTO Projects VALUES('CS300', 1, '完了');
INSERT INTO Projects VALUES('CS300', 2, '待機');
INSERT INTO Projects VALUES('CS300', 3, '待機');
INSERT INTO Projects VALUES('DY400', 0, '完了');
INSERT INTO Projects VALUES('DY400', 1, '完了');
INSERT INTO Projects VALUES('DY400', 2, '完了');

/* 列に対する量化:オール1の行を探せ */
CREATE TABLE ArrayTbl
 (keycol CHAR(1) PRIMARY KEY,
  col1  INTEGER,
  col2  INTEGER,
  col3  INTEGER,
  col4  INTEGER,
  col5  INTEGER,
  col6  INTEGER,
  col7  INTEGER,
  col8  INTEGER,
  col9  INTEGER,
  col10 INTEGER);

--オールNULL
INSERT INTO ArrayTbl VALUES('A', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO ArrayTbl VALUES('B', 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
--オール1
INSERT INTO ArrayTbl VALUES('C', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
--少なくとも一つは9
INSERT INTO ArrayTbl VALUES('D', NULL, NULL, 9, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO ArrayTbl VALUES('E', NULL, 3, NULL, 1, 9, NULL, NULL, 9, NULL, NULL);

--連番を作ろう
CREATE TABLE Digits
 (digit INTEGER PRIMARY KEY);

INSERT INTO Digits VALUES (0);
INSERT INTO Digits VALUES (1);
INSERT INTO Digits VALUES (2);
INSERT INTO Digits VALUES (3);
INSERT INTO Digits VALUES (4);
INSERT INTO Digits VALUES (5);
INSERT INTO Digits VALUES (6);
INSERT INTO Digits VALUES (7);
INSERT INTO Digits VALUES (8);
INSERT INTO Digits VALUES (9);

-- --欠番を全部求める
-- CREATE TABLE SeqTbl
--  (seq INTEGER PRIMARY KEY);
--
-- INSERT INTO SeqTbl VALUES (1);
-- INSERT INTO SeqTbl VALUES (2);
-- INSERT INTO SeqTbl VALUES (4);
-- INSERT INTO SeqTbl VALUES (5);
-- INSERT INTO SeqTbl VALUES (6);
-- INSERT INTO SeqTbl VALUES (7);
-- INSERT INTO SeqTbl VALUES (8);
-- INSERT INTO SeqTbl VALUES (11);
-- INSERT INTO SeqTbl VALUES (12);

--3人なんですけど、座れますか?
CREATE TABLE Seats
(seat   INTEGER NOT NULL  PRIMARY KEY,
 status CHAR(2) NOT NULL
 CHECK (status IN ('空', '占')) );

INSERT INTO Seats VALUES (1,  '占');
INSERT INTO Seats VALUES (2,  '占');
INSERT INTO Seats VALUES (3,  '空');
INSERT INTO Seats VALUES (4,  '空');
INSERT INTO Seats VALUES (5,  '空');
INSERT INTO Seats VALUES (6,  '占');
INSERT INTO Seats VALUES (7,  '空');
INSERT INTO Seats VALUES (8,  '空');
INSERT INTO Seats VALUES (9,  '空');
INSERT INTO Seats VALUES (10, '空');
INSERT INTO Seats VALUES (11, '空');
INSERT INTO Seats VALUES (12, '占');
INSERT INTO Seats VALUES (13, '占');
INSERT INTO Seats VALUES (14, '空');
INSERT INTO Seats VALUES (15, '空');

--折り返しも考慮
CREATE TABLE Seats2
 ( seat   INTEGER NOT NULL  PRIMARY KEY,
   row_id CHAR(1) NOT NULL,
   status CHAR(2) NOT NULL
     CHECK (status IN ('空', '占')) );

INSERT INTO Seats2 VALUES (1, 'A', '占');
INSERT INTO Seats2 VALUES (2, 'A', '占');
INSERT INTO Seats2 VALUES (3, 'A', '空');
INSERT INTO Seats2 VALUES (4, 'A', '空');
INSERT INTO Seats2 VALUES (5, 'A', '空');
INSERT INTO Seats2 VALUES (6, 'B', '占');
INSERT INTO Seats2 VALUES (7, 'B', '占');
INSERT INTO Seats2 VALUES (8, 'B', '空');
INSERT INTO Seats2 VALUES (9, 'B', '空');
INSERT INTO Seats2 VALUES (10,'B', '空');
INSERT INTO Seats2 VALUES (11,'C', '空');
INSERT INTO Seats2 VALUES (12,'C', '空');
INSERT INTO Seats2 VALUES (13,'C', '空');
INSERT INTO Seats2 VALUES (14,'C', '占');
INSERT INTO Seats2 VALUES (15,'C', '空');

--最大何人まで座れますか?
CREATE TABLE Seats3
 ( seat   INTEGER NOT NULL  PRIMARY KEY,
   status CHAR(2) NOT NULL
     CHECK (status IN ('空', '占')) );

INSERT INTO Seats3 VALUES (1,  '占');
INSERT INTO Seats3 VALUES (2,  '空');
INSERT INTO Seats3 VALUES (3,  '空');
INSERT INTO Seats3 VALUES (4,  '空');
INSERT INTO Seats3 VALUES (5,  '空');
INSERT INTO Seats3 VALUES (6,  '占');
INSERT INTO Seats3 VALUES (7,  '空');
INSERT INTO Seats3 VALUES (8,  '占');
INSERT INTO Seats3 VALUES (9,  '空');
INSERT INTO Seats3 VALUES (10, '空');

--単調増加と単調減少
CREATE TABLE MyStock
 (deal_date  DATE PRIMARY KEY,
  price      INTEGER );

INSERT INTO MyStock VALUES ('2007-01-06', 1000);
INSERT INTO MyStock VALUES ('2007-01-08', 1050);
INSERT INTO MyStock VALUES ('2007-01-09', 1050);
INSERT INTO MyStock VALUES ('2007-01-12', 900);
INSERT INTO MyStock VALUES ('2007-01-13', 880);
INSERT INTO MyStock VALUES ('2007-01-14', 870);
INSERT INTO MyStock VALUES ('2007-01-16', 920);
INSERT INTO MyStock VALUES ('2007-01-17', 1000);

--各隊、総員点呼!
CREATE TABLE Teams
(member  CHAR(12) NOT NULL PRIMARY KEY,
 team_id INTEGER  NOT NULL,
 status  CHAR(8)  NOT NULL);

INSERT INTO Teams VALUES('ジョー',   1, '待機');
INSERT INTO Teams VALUES('ケン',     1, '出動中');
INSERT INTO Teams VALUES('ミック',   1, '待機');
INSERT INTO Teams VALUES('カレン',   2, '出動中');
INSERT INTO Teams VALUES('キース',   2, '休暇');
INSERT INTO Teams VALUES('ジャン',   3, '待機');
INSERT INTO Teams VALUES('ハート',   3, '待機');
INSERT INTO Teams VALUES('ディック', 3, '待機');
INSERT INTO Teams VALUES('ベス',     4, '待機');
INSERT INTO Teams VALUES('アレン',   5, '出動中');
INSERT INTO Teams VALUES('ロバート', 5, '休暇');
INSERT INTO Teams VALUES('ケーガン', 5, '待機');

--一意集合と多重集合
CREATE TABLE Materials
(center         CHAR(12) NOT NULL,
 receive_date   DATE     NOT NULL,
 material       CHAR(12) NOT NULL,
 PRIMARY KEY(center, receive_date));

INSERT INTO Materials VALUES('東京'   ,'2007-4-01',   '錫');
INSERT INTO Materials VALUES('東京'   ,'2007-4-12',   '亜鉛');
INSERT INTO Materials VALUES('東京'   ,'2007-5-17',   'アルミニウム');
INSERT INTO Materials VALUES('東京'   ,'2007-5-20',   '亜鉛');
INSERT INTO Materials VALUES('大阪'   ,'2007-4-20',   '銅');
INSERT INTO Materials VALUES('大阪'   ,'2007-4-22',   'ニッケル');
INSERT INTO Materials VALUES('大阪'   ,'2007-4-29',   '鉛');
INSERT INTO Materials VALUES('名古屋', '2007-3-15',    'チタン');
INSERT INTO Materials VALUES('名古屋', '2007-4-01',    '炭素鋼');
INSERT INTO Materials VALUES('名古屋', '2007-4-24',    '炭素鋼');
INSERT INTO Materials VALUES('名古屋', '2007-5-02',    'マグネシウム');
INSERT INTO Materials VALUES('名古屋', '2007-5-10',    'チタン');
INSERT INTO Materials VALUES('福岡'   ,'2007-5-10',   '亜鉛');
INSERT INTO Materials VALUES('福岡'   ,'2007-5-28',   '錫');
--
-- --欠番を探せ:発展版
-- CREATE TABLE SeqTbl
-- ( seq INTEGER NOT NULL PRIMARY KEY);
--
-- --歯抜けなし:開始値が1
-- DELETE FROM SeqTbl;
-- INSERT INTO SeqTbl VALUES(1);
-- INSERT INTO SeqTbl VALUES(2);
-- INSERT INTO SeqTbl VALUES(3);
-- INSERT INTO SeqTbl VALUES(4);
-- INSERT INTO SeqTbl VALUES(5);
--
-- --歯抜けあり:開始値が1
-- DELETE FROM SeqTbl;
-- INSERT INTO SeqTbl VALUES(1);
-- INSERT INTO SeqTbl VALUES(2);
-- INSERT INTO SeqTbl VALUES(4);
-- INSERT INTO SeqTbl VALUES(5);
-- INSERT INTO SeqTbl VALUES(8);
--
-- --歯抜けなし:開始値が1ではない
-- DELETE FROM SeqTbl;
-- INSERT INTO SeqTbl VALUES(3);
-- INSERT INTO SeqTbl VALUES(4);
-- INSERT INTO SeqTbl VALUES(5);
-- INSERT INTO SeqTbl VALUES(6);
-- INSERT INTO SeqTbl VALUES(7);
--
-- --歯抜けあり:開始値が1ではない
-- DELETE FROM SeqTbl;
-- INSERT INTO SeqTbl VALUES(3);
-- INSERT INTO SeqTbl VALUES(4);
-- INSERT INTO SeqTbl VALUES(7);
-- INSERT INTO SeqTbl VALUES(8);
-- INSERT INTO SeqTbl VALUES(10);

--集合にきめ細かな条件を設定する
CREATE TABLE TestResults
(student CHAR(12) NOT NULL PRIMARY KEY,
 class   CHAR(1)  NOT NULL,
 sex     CHAR(1)  NOT NULL,
 score   INTEGER  NOT NULL);

INSERT INTO TestResults VALUES('001', 'A', '男', 100);
INSERT INTO TestResults VALUES('002', 'A', '女', 100);
INSERT INTO TestResults VALUES('003', 'A', '女',  49);
INSERT INTO TestResults VALUES('004', 'A', '男',  30);
INSERT INTO TestResults VALUES('005', 'B', '女', 100);
INSERT INTO TestResults VALUES('006', 'B', '男',  92);
INSERT INTO TestResults VALUES('007', 'B', '男',  80);
INSERT INTO TestResults VALUES('008', 'B', '男',  80);
INSERT INTO TestResults VALUES('009', 'B', '女',  10);
INSERT INTO TestResults VALUES('010', 'C', '男',  92);
INSERT INTO TestResults VALUES('011', 'C', '男',  80);
INSERT INTO TestResults VALUES('012', 'C', '女',  21);
INSERT INTO TestResults VALUES('013', 'D', '女', 100);
INSERT INTO TestResults VALUES('014', 'D', '女',   0);
INSERT INTO TestResults VALUES('015', 'D', '女',   0);

-- CREATE TABLE Teams
-- (member VARCHAR(32) PRIMARY KEY,
--  team   CHAR(1)     NOT NULL,
--  age    INTEGER     NOT NULL);
--
-- INSERT INTO Teams VALUES('大木',   'A',    28);
-- INSERT INTO Teams VALUES('逸見',   'A',    19);
-- INSERT INTO Teams VALUES('新藤',   'A',    23);
-- INSERT INTO Teams VALUES('山田',   'B',    40);
-- INSERT INTO Teams VALUES('久本',   'B',    29);
-- INSERT INTO Teams VALUES('橋田',   'C',    30);
-- INSERT INTO Teams VALUES('野々宮',      'D',    28);
-- INSERT INTO Teams VALUES('鬼塚',   'D',    28);
-- INSERT INTO Teams VALUES('加藤',   'D',    24);
-- INSERT INTO Teams VALUES('新城',   'D',    22);

-- CREATE TABLE Natural
-- (num INTEGER  NOT NULL PRIMARY KEY);
--
-- INSERT INTO Natural VALUES(0);
-- INSERT INTO Natural VALUES(1);
-- INSERT INTO Natural VALUES(2);
-- INSERT INTO Natural VALUES(3);
-- INSERT INTO Natural VALUES(4);
-- INSERT INTO Natural VALUES(5);
-- INSERT INTO Natural VALUES(6);
-- INSERT INTO Natural VALUES(7);
-- INSERT INTO Natural VALUES(8);
-- INSERT INTO Natural VALUES(9);
-- INSERT INTO Natural VALUES(10);

/* 演習問題1-1:複数列の最大値(1-3とも兼用) */
CREATE TABLE Greatests
(key CHAR(1) PRIMARY KEY,
 x   INTEGER NOT NULL,
 y   INTEGER NOT NULL,
 z   INTEGER NOT NULL);

INSERT INTO Greatests VALUES('A', 1, 2, 3);
INSERT INTO Greatests VALUES('B', 5, 5, 2);
INSERT INTO Greatests VALUES('C', 4, 7, 1);
INSERT INTO Greatests VALUES('D', 3, 3, 8);

/* 演習問題2-2:地域ごとのランキング */
CREATE TABLE DistrictProducts
(district  VARCHAR(16) NOT NULL,
 name      VARCHAR(16) NOT NULL,
 price     INTEGER NOT NULL,
 PRIMARY KEY(district, name));

INSERT INTO DistrictProducts VALUES('東北', 'みかん',    100);
INSERT INTO DistrictProducts VALUES('東北', 'りんご',    50);
INSERT INTO DistrictProducts VALUES('東北', 'ぶどう',    50);
INSERT INTO DistrictProducts VALUES('東北', 'レモン',    30);
INSERT INTO DistrictProducts VALUES('関東', 'レモン',    100);
INSERT INTO DistrictProducts VALUES('関東', 'パイン',    100);
INSERT INTO DistrictProducts VALUES('関東', 'りんご',    100);
INSERT INTO DistrictProducts VALUES('関東', 'ぶどう',    70);
INSERT INTO DistrictProducts VALUES('関西', 'レモン',    70);
INSERT INTO DistrictProducts VALUES('関西', 'スイカ',    30);
INSERT INTO DistrictProducts VALUES('関西', 'りんご',    20);

/* 演習問題2-3:ランキングの更新 */
CREATE TABLE DistrictProducts2
(district  VARCHAR(16) NOT NULL,
 name      VARCHAR(16) NOT NULL,
 price     INTEGER NOT NULL,
 ranking   INTEGER,
 PRIMARY KEY(district, name));

INSERT INTO DistrictProducts2 VALUES('東北', 'みかん',   100, NULL);
INSERT INTO DistrictProducts2 VALUES('東北', 'りんご',   50 , NULL);
INSERT INTO DistrictProducts2 VALUES('東北', 'ぶどう',   50 , NULL);
INSERT INTO DistrictProducts2 VALUES('東北', 'レモン',   30 , NULL);
INSERT INTO DistrictProducts2 VALUES('関東', 'レモン',   100, NULL);
INSERT INTO DistrictProducts2 VALUES('関東', 'パイン',   100, NULL);
INSERT INTO DistrictProducts2 VALUES('関東', 'りんご',   100, NULL);
INSERT INTO DistrictProducts2 VALUES('関東', 'ぶどう',   70 , NULL);
INSERT INTO DistrictProducts2 VALUES('関西', 'レモン',   70 , NULL);
INSERT INTO DistrictProducts2 VALUES('関西', 'スイカ',   30 , NULL);
INSERT INTO DistrictProducts2 VALUES('関西', 'りんご',   20 , NULL);

/* 8-1:配列テーブル――行持ちの場合 */
CREATE TABLE ArrayTbl2
 (key   CHAR(1) NOT NULL,
    i   INTEGER NOT NULL,
  val   INTEGER,
  PRIMARY KEY (key, i));

/* AはオールNULL、Bは一つだけ非NULL、Cはオール非NULL */
INSERT INTO ArrayTbl2 VALUES('A', 1, NULL);
INSERT INTO ArrayTbl2 VALUES('A', 2, NULL);
INSERT INTO ArrayTbl2 VALUES('A', 3, NULL);
INSERT INTO ArrayTbl2 VALUES('A', 4, NULL);
INSERT INTO ArrayTbl2 VALUES('A', 5, NULL);
INSERT INTO ArrayTbl2 VALUES('A', 6, NULL);
INSERT INTO ArrayTbl2 VALUES('A', 7, NULL);
INSERT INTO ArrayTbl2 VALUES('A', 8, NULL);
INSERT INTO ArrayTbl2 VALUES('A', 9, NULL);
INSERT INTO ArrayTbl2 VALUES('A',10, NULL);
INSERT INTO ArrayTbl2 VALUES('B', 1, 3);
INSERT INTO ArrayTbl2 VALUES('B', 2, NULL);
INSERT INTO ArrayTbl2 VALUES('B', 3, NULL);
INSERT INTO ArrayTbl2 VALUES('B', 4, NULL);
INSERT INTO ArrayTbl2 VALUES('B', 5, NULL);
INSERT INTO ArrayTbl2 VALUES('B', 6, NULL);
INSERT INTO ArrayTbl2 VALUES('B', 7, NULL);
INSERT INTO ArrayTbl2 VALUES('B', 8, NULL);
INSERT INTO ArrayTbl2 VALUES('B', 9, NULL);
INSERT INTO ArrayTbl2 VALUES('B',10, NULL);
INSERT INTO ArrayTbl2 VALUES('C', 1, 1);
INSERT INTO ArrayTbl2 VALUES('C', 2, 1);
INSERT INTO ArrayTbl2 VALUES('C', 3, 1);
INSERT INTO ArrayTbl2 VALUES('C', 4, 1);
INSERT INTO ArrayTbl2 VALUES('C', 5, 1);
INSERT INTO ArrayTbl2 VALUES('C', 6, 1);
INSERT INTO ArrayTbl2 VALUES('C', 7, 1);
INSERT INTO ArrayTbl2 VALUES('C', 8, 1);
INSERT INTO ArrayTbl2 VALUES('C', 9, 1);
INSERT INTO ArrayTbl2 VALUES('C',10, 1);

/* 10-1:一意集合と多重集合の一般化 */
CREATE TABLE Materials2
(center VARCHAR(32) NOT NULL,
 receive_date DATE  NOT NULL,
 material VARCHAR(32) NOT NULL,
 orgland  VARCHAR(32) NOT NULL,
 PRIMARY KEY(center, receive_date, material));

INSERT INTO Materials2 VALUES('東京',   '2007-04-01', '錫',        'チリ');
INSERT INTO Materials2 VALUES('東京',   '2007-04-12', '亜鉛',       'タイ');
INSERT INTO Materials2 VALUES('東京',   '2007-05-17', 'アルミニウム',   'ブラジル');
INSERT INTO Materials2 VALUES('東京',   '2007-05-20', '亜鉛',       'タイ');
INSERT INTO Materials2 VALUES('大阪',   '2007-04-20', '銅',        'オーストラリア');
INSERT INTO Materials2 VALUES('大阪',   '2007-04-22', 'ニッケル',     '南アフリカ');
INSERT INTO Materials2 VALUES('大阪',   '2007-04-29', '鉛',        'インド');
INSERT INTO Materials2 VALUES('名古屋', '2007-03-15', 'チタン',   'ボリビア');
INSERT INTO Materials2 VALUES('名古屋', '2007-04-01', '炭素鋼',   'チリ');
INSERT INTO Materials2 VALUES('名古屋', '2007-04-24', '炭素鋼',   'アルゼンチン');
INSERT INTO Materials2 VALUES('名古屋', '2007-05-02', 'マグネシウム',    'チリ');
INSERT INTO Materials2 VALUES('名古屋', '2007-05-10', 'チタン',   'タイ');
INSERT INTO Materials2 VALUES('福岡',   '2007-05-10', '亜鉛',       'アメリカ');
INSERT INTO Materials2 VALUES('福岡',   '2007-05-28', '錫',        'ロシア');

-- /* 演習問題10-2:行によって条件が異なる特性関数 */
-- CREATE TABLE TestScores
--  (student_id INTEGER NOT NULL,
--   subject    VARCHAR(16) NOT NULL,
--   score      INTEGER NOT NULL,
--     PRIMARY KEY (student_id, subject));
--
-- INSERT INTO TestScores VALUES(100, '算数', 100);
-- INSERT INTO TestScores VALUES(100, '国語', 80);
-- INSERT INTO TestScores VALUES(100, '理科', 80);
-- INSERT INTO TestScores VALUES(200, '算数', 80);
-- INSERT INTO TestScores VALUES(200, '国語', 95);
-- INSERT INTO TestScores VALUES(300, '算数', 40);
-- INSERT INTO TestScores VALUES(300, '国語', 50);
-- INSERT INTO TestScores VALUES(300, '社会', 55);
-- INSERT INTO TestScores VALUES(400, '算数', 80);