01
関係データベースとSELECTの基本
関係データベースの用語
関係データベース(リレーショナルデータベース)は、データをテーブル(行と列でできた表)に入れ、テーブル同士をキーの値でつなぎます。理論の世界では同じものを別の名前で呼ぶので、対応を覚えておくと便利です。
| 理論(関係モデル) | テーブルでは | 説明 |
|---|---|---|
| リレーション | テーブル | 同じ構造の行を集めたもの |
| タプル | 行 | インスタンス1つ |
| 属性(アトリビュート) | 列(カラム) | 性質1つ |
| 次数(Degree) | 列の数 | 属性の個数 |
| カーディナリティ(Cardinality) | 行の数 | タプルの個数 |
| ドメイン | 列の値の範囲 | 属性が取りうる値の集合 |
リレーションには決まった性質があります。まったく同じタプルが2つあってはならず、タプルの間にも属性の間にも順序はなく、すべての値はそれ以上分けられない原子値です。
キーにもいくつか種類があります。
| キー | 条件 |
|---|---|
| スーパーキー | 一意性だけを満たす — 行を区別できる列の組み合わせすべて |
| 候補キー | 一意性 + 極小性 — 主キーになれる候補 |
| 主キー(PK) | 候補キーの中から代表として選んだもの。NULLも重複も不可 |
| 代替キー | 主キーに選ばれなかった残りの候補キー |
| 外部キー(FK) | ほかのテーブルの主キーを参照する列 |
データがいつも正しい状態であるように守るルールが整合性制約です。主キーはNULLでも重複でもいけないというエンティティ整合性、外部キーの値は参照先のテーブルにある値かNULLでなければならないという参照整合性、列の値は決められたドメインの中になければならないというドメイン整合性が代表的です。
関係データベースの理論上の演算を関係代数といいます。SQLのそれぞれの部分がどの演算にあたるかを対応させると、次のようになります。
| 種類 | 演算 | 記号 | SQLでは |
|---|---|---|---|
| 純粋関係演算子 | SELECT(選択) | σ | WHERE — 条件に合う行を選ぶ |
| PROJECT(射影) | π | SELECTリスト — 欲しい列を選ぶ | |
| JOIN(結合) | ⋈ | JOIN — 共通の値で2つのリレーションをつなぐ | |
| DIVISION(商) | ÷ | 割る側の値をすべて持つ行だけを残す(直接対応する構文はない) | |
| 一般集合演算子 | 和 / 積 / 差 | ∪ / ∩ / − | UNION / INTERSECT / MINUS(EXCEPT) |
| 直積(デカルト積) | × | CROSS JOIN |
SQL文の4つの種類
| 種類 | コマンド | 役割 |
|---|---|---|
| DML(データ操作言語) | SELECT, INSERT, UPDATE, DELETE, MERGE | データを検索し、入れ、直し、消す。SELECTだけを分けてDQLと呼ぶこともある |
| DDL(データ定義言語) | CREATE, ALTER, DROP, RENAME, TRUNCATE | テーブルなどの構造を作り、変え、なくす |
| DCL(データ制御言語) | GRANT, REVOKE | 権限を与え、取り消す |
| TCL(トランザクション制御言語) | COMMIT, ROLLBACK, SAVEPOINT | 変更を確定する、または元に戻す |
SELECTとWHERE
SELECTの骨組みは次のとおりです。
SELECT [DISTINCT] 列 [AS 別名], ... FROM テーブル WHERE 条件 ORDER BY 列 [ASC | DESC];
- DISTINCTは結果から重複した行を取り除く(SELECTリスト全体が同じときに重複とみなす)
- 別名(エイリアス)はASで付け、空白や特殊文字を含むときはダブルクォーテーションで囲む
- 文字列をつなぐとき、Oracleは||、SQL Serverは+を使う(CONCAT関数はどちらにもある)
WHEREで使う演算子は次のとおりです。
| 演算子 | 意味 | 注意 |
|---|---|---|
| =, <>, !=, ^=, >, <, >=, <= | 比較 | <>、!=、^=はどれも「等しくない」 |
| BETWEEN a AND b | a以上b以下 | 両端の値を含む。小さいほうの値を先に書く |
| IN (a, b, c) | リストのどれかと等しい | NOT INのリストにNULLがあると、結果が1行も出ない |
| LIKE '山%' | パターン | %は0文字以上の任意の文字、_はちょうど1文字。%そのものを探すならESCAPE |
| IS NULL / IS NOT NULL | NULLかどうか | = NULLは常に「不明」になり、1行も出ない |
条件をいくつもつなぐときは優先順位があります。Oracleのドキュメントでは括弧 → 比較演算子 → NOT → AND → ORの順です。ANDがORより先なので、A OR B AND CはA OR (B AND C)として計算されます。迷ったら括弧を付けておくのが安全です。
ORDER BY:並べ替えとNULLの位置
ORDER BYは昇順(ASC)が既定で、列名・別名・SELECTリストでの位置番号(1、2…)で並べ替えられます。違いが出るのはNULLです。OracleはNULLを最大の値とみなして昇順なら最後に、SQL Serverは最小の値とみなして最初に置きます。Oracleでは、NULLS FIRST・NULLS LASTで位置を直接指定できます。
SELECT文が処理される順番
SELECT文は、書いた順番どおりには処理されません。この順番を知っておくと、「なぜWHEREで別名が使えないの?」「なぜWHEREにSUMを書けないの?」といった疑問がまとめて解けます。下の図で段階を進めてみてください。
SELECT DEPT_CODE, SUM(SALARY) AS TOTAL▶FROM EMPWHERE SALARY >= 260GROUP BY DEPT_CODEHAVING SUM(SALARY) >= 280ORDER BY TOTAL DESC;
EMP
| EMP_NO | NAME | DEPT_CODE | SALARY |
|---|---|---|---|
| 101 | 葵 | D10 | 300 |
| 102 | 健太 | D10 | 250 |
| 103 | 翔太 | D20 | 400 |
| 104 | 結衣 | D20 | 350 |
| 105 | 拓海 | NULL | 280 |
| 106 | 陽菜 | D30 | 260 |
1番目に処理 — FROM
- 処理の順番
- FROM
1段階ずつ進めてみてください。書く順番(SELECTがいちばん上)と、処理される順番は違います。
まずFROMが処理され、EMPテーブルの6行がすべて読み込まれます。SELECTはいちばん上に書きますが、実際に処理されるのは最後のほうです。
まとめると、FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BYです。別名はSELECTで生まれるので、その後のORDER BYでしか使えません。
問題文にこんな表現があれば → これを選ぶ
- “リレーションの列の数 / 行の数”次数 / カーディナリティ
- “一意性と極小性の両方を満たす”候補キー
- “外部キーは参照先テーブルの値かNULL”参照整合性
- “欲しい列だけを選ぶ関係演算”PROJECT(射影、π)
- “GRANT・REVOKE”DCL
- “BETWEEN 100 AND 200”100と200を含む
- “NOT INのリストにNULL”結果は0件
- “Oracleの昇順でのNULLの位置”最後(SQL Serverは最初)
- “WHEREでSELECTの別名を使えない理由”WHEREがSELECTより先に処理される
02
関数とGROUP BY・HAVING
SELECTで列を取り出せたら、次は値を加工し(関数)、まとめて数えます(GROUP BY)。
単一行関数:1行ごとに値を1つ返す
関数には2つの種類があります。1行ごとに結果を1つずつ返す単一行関数と、複数の行をまとめて結果を1つ返す複数行関数(集約関数)です。単一行関数はSELECT・WHERE・ORDER BYのどこでも使え、関数の中に関数を入れることもできます。
| 種類 | Oracle | SQL Server | 例と結果 |
|---|---|---|---|
| 文字 | LOWER, UPPER | LOWER, UPPER | UPPER('sql') → 'SQL' |
| SUBSTR | SUBSTRING | SUBSTR('DATABASE', 3, 4) → 'TABA' | |
| LENGTH | LEN | LENGTH('SQL') → 3 | |
| LTRIM, RTRIM, TRIM | LTRIM, RTRIM, TRIM | TRIM(' A ') → 'A' | |
| CONCAT, || | CONCAT, + | 'A' || 'B' → 'AB' | |
| 数値 | ROUND, TRUNC | ROUND | ROUND(38.567, 1) → 38.6 / TRUNC(38.567, 1) → 38.5 |
| CEIL, FLOOR | CEILING, FLOOR | CEIL(3.1) → 4 / FLOOR(-3.1) → -4 | |
| MOD | % | MOD(7, 3) → 1 | |
| 日付 | SYSDATE | GETDATE() | いまの日付と時刻 |
| EXTRACT | DATEPART | EXTRACT(YEAR FROM HIRE_DATE) |
Oracleでは、日付に数値を足すと日単位で計算されます。HIRE_DATE + 1は翌日、HIRE_DATE + 1/24は1時間後です。もう1つ、Oracleは空文字列(‘’)をNULLとして扱いますが、SQL Serverは空文字列とNULLを区別します。
型変換とCASE
データ型を変える方法は2つあります。関数で自分で変える明示的な型変換(Oracle:TO_CHAR・TO_NUMBER・TO_DATE、SQL Server:CAST・CONVERT)と、データベースが自動で変える暗黙的な型変換です。暗黙的な型変換は便利ですが、思いがけない形に変わったり、索引(インデックス)が使えなくなったりすることがあるので、明示的に書く習慣をつけておくと安心です。
条件によって違う値を返したいときはCASEを使います。1つの値と比べる単純CASE式と、条件式を書く検索CASE式があります。Oracleには同じことをするDECODE関数もあります。
SELECT NAME,
CASE WHEN SALARY >= 350 THEN '高'
WHEN SALARY >= 280 THEN '中'
ELSE '低' END AS GRADE, -- ELSEがなければNULL
DECODE(DEPT_CODE, 'D10', '営業', 'D20', '開発', 'その他') AS DEPT_NAME
FROM EMP;NULLを扱う関数
| 関数 | 返す値 | 例 |
|---|---|---|
| NVL(a, b) / ISNULL(a, b) | aがNULLならb、そうでなければa | NVL(BONUS, 0) |
| NVL2(a, b, c) | aがNULLでなければb、NULLならc(Oracle) | NVL2(BONUS, 'あり', 'なし') |
| NULLIF(a, b) | aとbが等しければNULL、違えばa | NULLIF(10, 10) → NULL |
| COALESCE(a, b, c, ...) | 最初に出てくるNULLでない値 | COALESCE(NULL, NULL, 3) → 3 |
集約関数とNULL
COUNT・SUM・AVG・MAX・MINのような集約関数は、複数の行をまとめて値を1つ作ります。覚えておくルールは1つです。COUNT(*)以外は、どれもNULLを無視します。下の図で関数を切り替えてみてください。
EMP
| NAME | SALARY | BONUS |
|---|---|---|
| 葵 | 300 | 50 |
| 健太 | 250 | NULL |
| 翔太 | 400 | NULL |
| 結衣 | 350 | 30 |
| 拓海 | 280 | NULL |
| 陽菜 | 260 | 20 |
COUNT(*) = 6
- 計算に使った行
- 6 / 6
関数を切り替えてみてください。緑の行は計算に入った行、取り消し線の行は外された行です。
COUNT(*)は行そのものを数えます。BONUSがNULLの行も行には違いないので、答えは6です。
集約関数の中にDISTINCTを入れると、重複した値は1回だけ数えます(COUNT(DISTINCT DEPT_CODE))。条件に合う行が1つもないとき、COUNTは0を、SUM・AVG・MAX・MINはNULLを返します。
GROUP BYとHAVING
GROUP BYは、同じ値を持つ行をまとめて、グループごとに集計します。まとめた後はグループごとに1行になるので、いくつかルールが生まれます。
- SELECTリストで集約関数の外にある列は、必ずGROUP BYにも書く
- GROUP BYにはSELECTの別名を書かない — 処理の順番では、まだ別名がない(最新のOracle 23aiでは使えるが、試験は従来のルールが基準)
- NULLも1つのグループになる
- GROUP BYは並び順を保証しない — 順番が必要ならORDER BYを書く
グループに条件を付けるときはHAVINGを使います。WHEREはまとめる前に行を絞り、HAVINGはまとめた後にグループを絞ります。だから、集約関数を使った条件はHAVINGにしか書けません。集約関数が要らない条件なら、WHEREで先に絞っておいたほうが処理する行が減って有利です。
SELECT DEPT_CODE, COUNT(*) AS HEADCOUNT, AVG(SALARY) AS AVG_SAL FROM EMP WHERE SALARY >= 250 -- 先に行を絞る GROUP BY DEPT_CODE HAVING COUNT(*) >= 2; -- まとめた後にグループを絞る
問題文にこんな表現があれば → これを選ぶ
- “SUBSTR('DATABASE', 3, 4)”'TABA'
- “Oracleでの空文字列”NULLとして扱われる
- “aとbが等しければNULL”NULLIF(a, b)
- “最初に出てくるNULLでない値”COALESCE
- “NULLも数える集約関数”COUNT(*)だけ
- “AVG(BONUS)とAVG(NVL(BONUS, 0))”割る行数が違う
- “集約関数の条件を書く句”HAVING
- “GROUP BYにない列をSELECTに”集約関数で包まないとエラー
Pro限定
ここまでは無料で読めます
残りの5章はPro会員向けです。各章に3D図解と「問題文にこんな表現があれば」の整理が続き、最後の章では実際に解いて確かめられます。
- 03結合
- 04副問合せ・ビュー・集合演算子
- 05グループ関数・ウィンドウ関数・Top N
- 06自己結合・階層問合せ・PIVOT・正規表現
- 07管理系の文:DML・TCL・DDL・DCL
広告
PasslyExam
概念をつかんだら、次は試験形式の問題で確かめましょう
PasslyExamは、各資格の出題範囲に合わせて作成したオリジナルの練習問題・解説・本番形式の模擬試験を提供しています。正解の選択肢がなぜ正しく、似た選択肢がなぜ誤りなのかまで解説します。登録すると毎日無料で練習問題に取り組めます。
この概念が出題される試験