SQLの基本

SQLは、データベースにデータを問い合わせたり、入れたり、直したりするための言語です。このガイドでは、SELECT文が処理される順番から始めて、関数とGROUP BY、結合、副問合せ、ウィンドウ関数、階層問合せを経て、データを変更して確定させる管理系の文までを順番に見ていきます。例には社員テーブルと部署テーブルをずっと使い、章ごとに自分で操作できる図を用意しました。説明はOracleを基準にし、SQL Serverの動きが違うところは別に書いています。テーブルをどう分けて設計するかは、データモデリングのガイドで扱います。

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 ba以上b以下両端の値を含む。小さいほうの値を先に書く
IN (a, b, c)リストのどれかと等しいNOT INのリストにNULLがあると、結果が1行も出ない
LIKE '山%'パターン%は0文字以上の任意の文字、_はちょうど1文字。%そのものを探すならESCAPE
IS NULL / IS NOT NULLNULLかどうか= NULLは常に「不明」になり、1行も出ない

条件をいくつもつなぐときは優先順位があります。Oracleのドキュメントでは括弧 → 比較演算子 → NOT → AND → ORの順です。ANDがORより先なので、A OR B AND CはA OR (B AND C)として計算されます。迷ったら括弧を付けておくのが安全です。

NOT IN (10, 20, NULL)は、なぜ何も返さないのでしょうか。NOT INは「10ではない AND 20ではない AND NULLではない」に展開されますが、NULLとの比較は常に「不明」なので、条件全体が真になることがないからです。副問合せの結果にNULLが混ざる可能性があるなら、NOT EXISTSを使うか、NULLを除いておきます。

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 EMP
WHERE SALARY >= 260
GROUP BY DEPT_CODE
HAVING SUM(SALARY) >= 280
ORDER BY TOTAL DESC;

EMP

EMP_NONAMEDEPT_CODESALARY
101葵D10300
102健太D10250
103翔太D20400
104結衣D20350
105拓海NULL280
106陽菜D30260

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のどこでも使え、関数の中に関数を入れることもできます。

種類OracleSQL Server例と結果
文字LOWER, UPPERLOWER, UPPERUPPER('sql') → 'SQL'
SUBSTRSUBSTRINGSUBSTR('DATABASE', 3, 4) → 'TABA'
LENGTHLENLENGTH('SQL') → 3
LTRIM, RTRIM, TRIMLTRIM, RTRIM, TRIMTRIM(' A ') → 'A'
CONCAT, ||CONCAT, +'A' || 'B' → 'AB'
数値ROUND, TRUNCROUNDROUND(38.567, 1) → 38.6 / TRUNC(38.567, 1) → 38.5
CEIL, FLOORCEILING, FLOORCEIL(3.1) → 4 / FLOOR(-3.1) → -4
MOD%MOD(7, 3) → 1
日付SYSDATEGETDATE()いまの日付と時刻
EXTRACTDATEPARTEXTRACT(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、そうでなければaNVL(BONUS, 0)
NVL2(a, b, c)aがNULLでなければb、NULLならc(Oracle)NVL2(BONUS, 'あり', 'なし')
NULLIF(a, b)aとbが等しければNULL、違えばaNULLIF(10, 10) → NULL
COALESCE(a, b, c, ...)最初に出てくるNULLでない値COALESCE(NULL, NULL, 3) → 3

集約関数とNULL

COUNT・SUM・AVG・MAX・MINのような集約関数は、複数の行をまとめて値を1つ作ります。覚えておくルールは1つです。COUNT(*)以外は、どれもNULLを無視します。下の図で関数を切り替えてみてください。

EMP

NAMESALARYBONUS
葵30050
健太250NULL
翔太400NULL
結衣35030
拓海280NULL
陽菜26020

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図解と「問題文にこんな表現があれば」の整理が続き、最後の章では実際に解いて確かめられます。

  1. 03結合
  2. 04副問合せ・ビュー・集合演算子
  3. 05グループ関数・ウィンドウ関数・Top N
  4. 06自己結合・階層問合せ・PIVOT・正規表現
  5. 07管理系の文:DML・TCL・DDL・DCL

広告

PasslyExam

概念をつかんだら、次は試験形式の問題で確かめましょう

PasslyExamは、各資格の出題範囲に合わせて作成したオリジナルの練習問題・解説・本番形式の模擬試験を提供しています。正解の選択肢がなぜ正しく、似た選択肢がなぜ誤りなのかまで解説します。登録すると毎日無料で練習問題に取り組めます。

この概念が出題される試験

同じ分野のほかの解説

ほかの分野の解説

コンセプト解説の一覧を見る