良かったこと
SQLの文法を総ざらいできたことが、今回の大きな収穫です。これまでにSQLに関する参考書をいくつか読んでいたため、見覚えのある内容が多くありました。しかし、実際に演習問題に取り組んでみると、「あれ?これは想定と違う…」と感じる場面も少なくありませんでした。そういった問題に取り組むことで、自分の知識の穴を埋めることができたのは非常に良かったです。
そして、何よりも嬉しかったのは、問題数の多さ!多様な問題に取り組むことで、知識がしっかりと定着したと感じています。理論だけでなく、手を動かして問題を解く経験は、学びを深めるうえでとても重要だと実感しました。
難しかったこと
SQLの文法自体は基本的に理解していたため、演習を進めるうえで特に困難に感じることはありませんでした。しかし、巻末の256題の中には、「どう解けばいいんだろう?」と悩んでしまうような難易度の高い問題もいくつかあり、そこで手こずりました。
また、DB設計に関する部分は、実務経験が少ないこともあって、理論的には理解できても実践でどのように応用すべきか迷うことが多かったです。この部分については、今後さらに経験を積むことで克服していきたいと感じています。
読んで学んだこと
1~6章
- シングルクオーテーションで括らずに記述したリテラルは数値として扱われる。
- シングルクオーテーションで括ったリテラルは文字情報として扱われる。
- シングルクオーテーションで括られ、かつ
'2024-08-31'
のような一定の形式のリテラルは日付情報として扱われる。 CHAR
は固定長。例えばCHAR(10)
なら10バイト分領域が確保される。なので3文字だけ入れても残り7文字は空白として埋められる。VARCHAR
は可変長。入力文字の長さに合わせて領域が確保される。- 以上より、
CHAR
は固定長なので、格納するデータの長さが一定の場合(例:郵便番号、社員番号)に使うと良い。VARCHAR
は氏名や本の名前など、格納データの長さが決まっていないものに対して使うと良い。 - 社員番号のように社員テーブルの情報を設計する上で自然と出てくる主キーは自然キーという。
- 購入履歴テーブルのように、テーブル設計時に主キーが登場しない場合に作る主キーは人工キー、または代替キーという。
IN
句=ANY
NOT IN
=<>ANY
OFFSET
は先頭から除外する行数を指定し、FETCH NEXT
で取得したい行数を指定する。ANY
とIN
の違い:JOIN
とUNION
の違い:UNION
にはUNION
とUNION ALL
の2種類がある。UNION
は重複を削除して出力するため、UNION ALL
より遅くなる(和集合)。UNION ALL
は重複をそのまま出力。UNION
は結合するテーブルの列が同じ(列数が同じで、各列のデータ型が一致していれば結合可能)。データ型が一致していれば、異なるカラムでも結合できる。EXCEPT
は差集合、INTERSECT
は積集合。
UNION
は2つのSELECT
結果を縦に結合するイメージ。JOIN
は2つのテーブルを共通のカラムを基に横に繋げるイメージ。
CASE
演算子には2つの書き方がある```sql SELECT 費目, 出金額, CASE 費目 WHEN '居住費用' THEN '固定費' WHEN '水道光熱費' THEN '固定費' ELSE '変動費' END AS 出費の分類 FROM 家計簿 WHERE 出金額 > 0; ``` ```sql SELECT 費目, 入金額, CASE WHEN 入金額 < 5000 THEN 'お小遣い' WHEN 入金額 < 10000 THEN '一時収入' ELSE '想定外の収入です' END AS 収入の分類 FROM 家計簿 WHERE 入金額 > 0; ```
WHERE
とHAVING
:HAVING
は集計処理を行った後の結果に対して条件を絞る機能で、WHERE
と同じ条件で使用することができる。
COALESCE
関数:COALESCE
関数に渡された引数のうち、最初にNULL
でない値を返す。SELECT COALESCE('A', 'B', 'C'); -- 結果: A SELECT COALESCE(NULL, 'B', 'C'); -- 結果: B SELECT COALESCE(NULL, NULL, 'C'); -- 結果: C
ある列に
NULL
が格納されている行がある場合、通常のSELECT
では何も表示されない。そのような場合、COALESCE
関数を使用することで値を代替表示できる。SELECT 日付, 費用, COALESCE(メモ, 'NULLだよ!') AS メモ, 入金額, 出金額 FROM 家計簿;
TRUNC
関数:指定桁で切り捨てる。
SELECT TRUNC(123456, -1); -- 結果: 123450 SELECT TRUNC(123456, -2); -- 結果: 123400 SELECT TRUNC(123456, -3); -- 結果: 123000
ROUND
関数:- 指定桁で四捨五入する。 ```sql SELECT ROUND(123456, -1); -- 結果: 123460 SELECT ROUND(123456, -2); -- 結果: 123500 SELECT ROUND(123456, -3); -- 結果: 123000 ```
文字列の最後から指定した文字数を取得する場合、
RIGHT
関数を使用する。```sql SELECT RIGHT('abcdef', 2); -- 結果: 'ef' ```
LENGTH
関数を使って、文字列の長さに基づいて開始位置を計算することも可能。```sql SELECT SUBSTRING('abcdef', LENGTH('abcdef') - 2 + 1, 2); -- 結果: 'ef' ```
7章(副問い合わせ)
- 副問い合わせには3種類ある:
- 単一の値による問い合わせ、列挙の値の問い合わせ、表形式の問い合わせ。
- 単一の値の副問い合わせ(1行1列=1セル値を使う):
SELECT 家計簿集計 SET 平均 = (SELECT AVG(出金額) FROM 家計簿アーカイブ WHERE 出金額 > 0 AND 費目 = '食費') WHERE 費目 = '食費';
- 列挙の値の問い合わせ(n行1列の結果を使う):
SELECT * FROM 家計簿 WHERE 費目 IN (SELECT 費目 FROM 家計簿);
- 表形式の問い合わせ(n行m列の結果を使う):
SELECT SUM(SUB.出金額) AS 出金額合計 FROM ( SELECT 日付, 費目, 出金額 FROM 家計簿 UNION SELECT 日付, 費目, 出金額 FROM 家計簿アーカイブ WHERE 日付 >= '2024-01-01' AND 日付 <= '2024-01-31' ) AS SUB;
8章(複数テーブルの結合)
- テーブルを複数に分ける理由、メリット:
- 値が変更になった場合、その修正の負荷が大幅に軽減される。外部参照を使えば、別テーブルを更新するだけで済むが、1つのテーブルに全て詰め込むと、該当行全てを更新する必要がある。
- 1つのテーブルにさまざまな情報を詰め込むと管理が難しくなる。
- ヒューマンエラーが増える(1行1行更新するのは無謀)。
LEFT JOIN
:結合される側に合わせる結合の仕方。結合相手に該当行がない場合はNULL
で出力する。RIGHT JOIN
:結合相手に合わせる結合の仕方。結合される側の行がない場合はNULL
で出力する。INNER JOIN
:結合する両方のテーブルに存在する行だけを出力。
9章(トランザクション)
- ロック:
- トランザクション:
- トランザクションを使うことで、以下のような不具合を防ぐ:
- 友人にお金を振り込む際、自分の口座からお金を減らす操作と相手の口座にお金を増やす操作があり、途中で処理が中断すると、自分の口座からはお金が減ったが相手の口座には反映されない、といった問題を防ぐ。
トランザクションの指示:
BEGIN; -- 以降のSQLを1つのトランザクションとする指示 INSERT INTO 家計簿アーカイブ SELECT * FROM 家計簿 WHERE 日付 <= '2024-01-31'; DELETE FROM 家計簿 WHERE 日付 <= '2024-01-31'; COMMIT; -- ここまでの処理を1つのトランザクションとして確定する
ダーティーリード:
- 反復不能読み取り:
SELECT
文を2回実行する際に、他のトランザクションが値をUPDATE
してしまい、前後の結果が変わること。
- ファントムリード:
SELECT
文を2回実行する際に、他のトランザクションが新たな行をINSERT
してしまい、行数の結果が変わること。
- デッドロック:
- 2つのトランザクションがお互いにロックし合い、永遠に処理が完了しない状態。
10章(テーブルの作成)
- インデックス:
- 本の索引のように、インデックスを作成することでデータベース検索が高速化される。
- ビュー:
- 繰り返し同じSQLを実行するのは面倒なため、よく使う結果を保存しておく機能。
- DDL(データ定義言語):
CREATE
,ALTER
,DROP
,TRUNCATE
- DML(データ操作言語):
SELECT
,INSERT
,UPDATE
,DELETE
- TCL(トランザクション制御言語):
COMMIT
,ROLLBACK
,SET TRANSACTION
,SAVEPOINT
11章(さまざまな支援機能)
- 検索を高速化する方法:
- インデックスを作成する。
- SQLのインデックスによる高速化が期待できる場合:
WHERE
句にインデックス列を指定する場合。ORDER BY
にインデックス列を指定する場合。JOIN
の結合条件にインデックス列を指定する場合。
12章(テーブルの設計)
データベース構築のインプットとアウトプット
インプット:
- 要件やお客さんから得た情報。
アウトプット:
- DDL一式(必要十分なテーブルを生成する)。
データベース設計の大まかな流れ
- 要件を聞き、情報を集める。
- データベース設計作業。
- DDL作成。
概念設計:
- 管理する情報(データ)は何があるのかを明らかにする
- 言い換えるとエンティティとその属性を明らかにする作業。
- 「顧客情報」「入出金情報」「製品」などなど
- 具体的な作業としてはER図のを作るということ
- エンティティを明らかにする時のコツ
- 要件の中から候補となる用語を洗い出す
- その要件に登場する「人」「もの」「事実」「行為」が何かを考えつつ用語を出す
- 何かの具体でしかない用語はすてる(利用者があるなら、田中さんは捨てて良い)
- 計算や集計すれば出るものも捨てる
- 関連のある用語たちをまとめていく(日付、利用者、金額、内容などであれば入出金行為テーブルだなってかんじ)
- 大事なのは正解はないという前提を忘れないこと。お互いに自分なりの正解を見せ合い、意見交換し、良いものにしてく
論理設計:
- 概念設計で作ったエンティティとその関係をDBに登録して使えるように修正していく作業
- 流れとしては、多対多の分解→キーの整理→正規化の流れ
- 多対多の分解
- RDBは多対多を上手く扱えないのであればテーブルを分解する
- 1対多にする
- キーの整理
- 主キー、外部キーはどの列になるか整理する。主キーがなければ人工キーを作成する
- 正規化
- 正規化によってテーブルが適切に分割された状態を正規形という。
- 第1~第5正規形まで存在する
- 非正規系
- セルの結合がある、1セルに複数行ある
- つまり繰り返しの列がある状態
- 第一正規系
- テーブルの全行の全列に1つの値が入っている。
- 主キー以外の全列が、主キーに従属していること
- 第二正規系
- 複合主キーがある場合は複合主キー以外の列が複合主キーに綺麗に従属している
- 複合主キーの片方に従属している非主キーは存在しないようにする
- 第三正規系
- 間接的に主キーの従属している列を排除する
- 概念設計で作ったエンティティとその関係をDBに登録して使えるように修正していく作業
物理設計:
- 最終的なテーブル名、列名、データ型、制約、デフォルト値、インデックスを決定し、DBにテーブルを作成する。