はじめに
SQLの基本をまとめています。記事は随時更新予定です。
準備
SQLとは何か
SQL(Structured Query Language、構造化問い合わせ言語)とは、RDBMS(Relational Database Management System、関係データベース管理システム)によってRDB(Relational Database、関係データベース)内のデータを操作・管理するために使用される標準的な言語です[1]。
SQLは厳密にはプログラミング言語ではありません。アプリケーション内では、ほかのプログラミング言語と併用して使用されます。
データベースとは何か
データベースとは、電子的に保存され、アクセスできる組織化されたデータの集合です[2]。「組織化されたデータの集合」と言うと複雑そうですが、要するにデータ集合が何らかのルールに従っているというような認識で大まかにはよいと思います。そのルール次第で様々なデータベースが存在します。
関係データベースとは何か
データベースの中でも特によく用いられるのが関係データベースです。関係データベースは表形式のデータ構造をもつ複数のテーブルとそれらの関係によって構造化されます。テーブルの最も簡単なイメージはExcelの表です。テーブルには横の行(レコード)と縦の列(カラム)があります。
関係データベース管理システム(RDBMS)とは何か
関係データベースはRDBMSによって管理されます。主要なRDBMSにはOracle Database、Microsoft SQL Server、MySQL、PostgreSQLなどがあります。Microsoft SQL ServerはMicrosoftとの高い互換性で有名です。MySQLは根強い人気がありますが、近年はOSSのPostgreSQLがMySQLに追いつく勢いで人気を高めています。
SQLの分類
SQL文は、三種類に大別できます。
-
DDL(data definition language、データ定義言語)
-
CREATE:データベースオブジェクトの定義 -
ALTER:データベースオブジェクトの定義変更 -
DROP:データベースオブジェクトの削除
-
データ定義言語はテーブルを作成したり、構造を変更したり、削除したりするためのSQLです。テーブルだけでなく、データベースそのものや、ビュー、ストアドプロシージャなどのデータベースオブジェクトも作成・変更・削除することができます。
-
DML(data manipulation language、データ操作言語)
-
SELECT:データベースのデータを取得 -
INSERT:データベースにデータを追加 -
UPDATE:データベースに保存されているデータを更新 -
DELETE:データベースに保存されているデータを削除
-
データ操作言語はデータベースのデータを自由に操作するための基本的な文法です。
-
DCL(data control language、データ制御言語)
-
BEGIN:トランザクションを開始 -
COMMIT:トランザクションを確定 -
ROLLBACK:トランザクションを取り消し
-
データ制御言語は、トランザクションを制御するためのSQLです。トランザクションとは、複数のSQL文をひとまとまりのデータ処理として扱うための仕組みです。このようにSQL文をまとめて扱うことで処理が部分的に失敗してデータの整合性や一貫性が失われることを防ぐことができます。
トランザクションはACID特性、つまり原子性(Atomicity)、一貫性(Consistency)、独立性(Isolation)、永続性(Durability)を持つべきものとして考えられています。
SQLの命名規則
SQLは原則として大文字と小文字を区別しません。したがって、変数名の重複には注意する必要があります。
細かい命名規則はプロジェクトや使用するRDBMSによって少しづつ変わると思います。しかし、複合語を使うときはキャメルケース(UserName、userName)やケバブケース(user-name)ではなくスネークケース(USER_NAME、user_name)にし、大文字か小文字に統一するのが一般的です。
また、明確な決まりはありませんが、慣習として、キーワードを大文字、テーブル名やカラム名を小文字に統一することがあります。以降はそのスタイルで記述します。
実行環境
本記事では実行環境にpaizaのオンライン・実行環境を使用します。こちらのページに進み、左上の緑の言語選択ボタンからMySQLを選択すれば準備完了です。登録は必要ありません。すでにRDBMSをお使いの方は普段の環境で大丈夫です。
SQLはRDBMSの扱い、ひいては環境設定と切っても切り離せないと思います。しかし、本記事の目的はSQLの最も基本的な文法をまとめることにありますし、RDBMSの説明は手に余ると感じています。そこで、あえてここでは環境については詳しく書きません。
レベル1
テーブルの作成:CREATE TABLE
CREATE TABLEで新しいテーブルを作成します。
CREATE TABLE employee (
id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
furigana VARCHAR(50) NOT NULL,
gender CHAR(1) NOT NULL CHECK (gender IN ('F', 'M')),
birth_date DATE NOT NULL
);
ここでは5つのカラム名(id、name、furigana、gender、birth_date)とカラムに格納されるべきデータの種類が定義されています。例えばfuriganaには50文字以下の可変長文字列(VARCHAR(50))であって、かつNULLでない(NOT NULL)のデータが格納されます。それ以外のデータを格納しようとするとはじかれてしまいます。
また、SQLの重要な概念として主キー(PRIMARY KEY)というものがあります。これはSQLのレコードを管理するために使われる最も重要な通し番号です。ここではidがPRIMARY KEYとして定義されています。
データの追加:INSERT INTO
作成したテーブルにデータを挿入します。
CREATE TABLE employee (
id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
furigana VARCHAR(50) NOT NULL,
gender CHAR(1) NOT NULL CHECK (gender IN ('F', 'M')),
birth_date DATE NOT NULL
);
INSERT INTO employee (id, name, furigana, gender, birth_date) VALUES
(2, 'Hanako Sato', 'サトウ ハナコ', 'F', '1988-10-15'),
(1, 'Taro Tanaka', 'タナカ タロウ', 'M', '1985-04-01'),
(4, 'Mari Takahashi', 'タカハシ マリ', 'F', '1990-01-05'),
(3, 'Ichiro Suzuki', 'スズキ イチロウ', 'M', '1992-06-20');
データの取得:SELECT~FROM
テーブルのすべてのデータを取得するにはワイルドカード*を使用してこのように記述します。
CREATE TABLE employee (
id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
furigana VARCHAR(50) NOT NULL,
gender CHAR(1) NOT NULL CHECK (gender IN ('F', 'M')),
birth_date DATE NOT NULL
);
INSERT INTO employee (id, name, furigana, gender, birth_date) VALUES
(2, 'Hanako Sato', 'サトウ ハナコ', 'F', '1988-10-15'),
(1, 'Taro Tanaka', 'タナカ タロウ', 'M', '1985-04-01'),
(4, 'Mari Takahashi', 'タカハシ マリ', 'F', '1990-01-05'),
(3, 'Ichiro Suzuki', 'スズキ イチロウ', 'M', '1992-06-20');
SELECT * FROM employee;
| id | name | furigana | gender | birth_date |
|---|---|---|---|---|
| 2 | Hanako Sato | サトウ ハナコ | F | 1988-10-15 |
| 1 | Taro Tanaka | タナカ タロウ | M | 1985-04-01 |
| 4 | Mari Takahashi | タカハシ マリ | F | 1990-01-05 |
| 3 | Ichiro Suzuki | スズキ イチロウ | M | 1992-06-20 |
以降、CREATEとINSERT INTOは省略します。SELECTではカラムを指定してidとnameだけを出力することもできます。
SELECT id, name FROM employee;
| id | name |
|---|---|
| 2 | Hanako Sato |
| 1 | Taro Tanaka |
| 4 | Mari Takahashi |
| 3 | Ichiro Suzuki |
条件の指定:WHERE
条件を指定して表示することもできます。
SELECT * FROM employee
WHERE gender = 'F';
| id | name | furigana | gender | birth_date |
|---|---|---|---|---|
| 2 | Hanako Sato | サトウ ハナコ | F | 1988-10-15 |
| 4 | Mari Takahashi | タカハシ マリ | F | 1990-01-05 |
データのソート:ORDER BY
先ほどまでのデータはidが昇順となっていませんでした。ORDER BY句を使えばデータをキーの値でソートして出力することもできます。
SELECT * FROM employee
ORDER BY id;
| id | name | furigana | gender | birth_date |
|---|---|---|---|---|
| 1 | Taro Tanaka | タナカ タロウ | M | 1985-04-01 |
| 2 | Hanako Sato | サトウ ハナコ | F | 1988-10-15 |
| 3 | Ichiro Suzuki | スズキ イチロウ | M | 1992-06-20 |
| 4 | Mari Takahashi | タカハシ マリ | F | 1990-01-05 |
誕生日順にすることもできます。
SELECT * FROM employee
ORDER BY birth_date;
| id | name | furigana | gender | birth_date |
|---|---|---|---|---|
| 1 | Taro Tanaka | タナカ タロウ | M | 1985-04-01 |
| 2 | Hanako Sato | サトウ ハナコ | F | 1988-10-15 |
| 4 | Mari Takahashi | タカハシ マリ | F | 1990-01-05 |
| 3 | Ichiro Suzuki | スズキ イチロウ | M | 1992-06-20 |
データの更新:UPDATE
サトウさんをサイトウさんに更新してみましょう。
UPDATE employee
SET name = 'Hanako Saito', furigana = 'サイトウ ハナコ'
WHERE id = 2;
SELECT * FROM employee
ORDER BY id;
| id | name | furigana | gender | birth_date |
|---|---|---|---|---|
| 1 | Taro Tanaka | タナカ タロウ | M | 1985-04-01 |
| 2 | Hanako Saito | サイトウ ハナコ | F | 1988-10-15 |
| 3 | Ichiro Suzuki | スズキ イチロウ | M | 1992-06-20 |
| 4 | Mari Takahashi | タカハシ マリ | F | 1990-01-05 |
データの削除:DELETE
サトウさんのデータを消してみましょう。
DELETE FROM employee WHERE id = 2;
SELECT * FROM employee
ORDER BY id;
| id | name | furigana | gender | birth_date |
|---|---|---|---|---|
| 1 | Taro Tanaka | タナカ タロウ | M | 1985-04-01 |
| 3 | Ichiro Suzuki | スズキ イチロウ | M | 1992-06-20 |
| 4 | Mari Takahashi | タカハシ マリ | F | 1990-01-05 |
レベル2
テーブルの結合①:INNER JOIN、JOIN
関係データベースでは複数のテーブルを使うことがあります。ここでは、社員テーブルと部門テーブルを例に解説します。
- 社員テーブル
CREATE TABLE employee (
id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
furigana VARCHAR(50) NOT NULL,
gender CHAR(1) NOT NULL CHECK (gender IN ('F', 'M')),
birth_date DATE NOT NULL,
dep_id INT NOT NULL
);
INSERT INTO employee (id, name, furigana, gender, birth_date,dep_id) VALUES
(1, 'Taro Tanaka', 'タナカ タロウ', 'M', '1985-04-01',1),
(2, 'Hanako Sato', 'サトウ ハナコ', 'F', '1988-10-15',1),
(3, 'Ichiro Suzuki', 'スズキ イチロウ', 'M', '1992-06-20',2),
(4, 'Mari Takahashi', 'タカハシ マリ', 'F', '1990-01-05',2),
(5, 'Kou Ishida', 'イシダ コウ', 'M', '1995-03-11',4);
- 部門テーブル
CREATE TABLE department (
dep_id INT PRIMARY KEY,
dep_name VARCHAR(50) NOT NULL
);
INSERT department (dep_id, dep_name) VALUES
(1, '人事部'),
(2, '営業部'),
(3, '開発部');
この二つのテーブルには共通するカラムdep_idがあります。これをもとにテーブルを結合するには下のようにします。
SELECT employee.id, employee.name, department.dep_id,department.dep_name
FROM employee
INNER JOIN department
ON employee.dep_id = department.dep_id;
同じ意味で下に書くこともできます。employeeにe、departmentにdという別名(alias)をそれぞれ付けています。Pythonでいうところのimport numpy as npのようなイメージかもしれません。SQLではasのような分かりやすい別名の目印がないので注意が必要です。
SELECT e.id, e.name, d.dep_id,d.dep_name
FROM employee e
INNER JOIN department d
ON e.dep_id = d.dep_id;
ちなみに、INNER JOINは省略して単にJOINと書いても同じ意味になります。
| id | name | dep_id | dep_name |
|---|---|---|---|
| 1 | Taro Tanaka | 1 | 人事部 |
| 2 | Hanako Sato | 1 | 人事部 |
| 3 | Ichiro Suzuki | 2 | 営業部 |
| 4 | Mari Takahashi | 2 | 営業部 |
ここで注意すべきなのは、departmentのほうでは存在する開発部がINNER JOINの後に消えているという点と、employeeには存在するイシダさんのレコードが消滅しているという点です。INNER JOINは結合部が両方のテーブルにあるものだけを抽出します。
イシダさんのdep_idは4ですが、departmentではdep_idは1と2と3しかありません。また、開発部のdep_idは3ですが、employeeテーブルの中にはdep_idが3であるような人はいません。そのような、いわば不完全なデータはINNER JOINでは除外されてしまいます。
テーブルの結合②:LEFT JOIN、LEFT OUTER JOIN
LEFT JOINは、左側のテーブルの全てのレコードを取得し、右側のテーブルで結合条件を満たすレコードが存在すればそのデータを結合します。右側に一致するデータがない場合、右側のカラムはNULLになります。
INNER JOINのコードのSELECT以下の部分を次のように書き換えると違いが分かります。
SELECT employee.id, employee.name, department.dep_id,department.dep_name
FROM employee
LEFT JOIN department
ON employee.dep_id = department.dep_id;
実行結果は以下のようになり、departmentには対応するdep_idを持たないイシダさんのデータが表示されるようになりました。
| id | name | dep_id | dep_name |
|---|---|---|---|
| 1 | Taro Tanaka | 1 | 人事部 |
| 2 | Hanako Sato | 1 | 人事部 |
| 3 | Ichiro Suzuki | 2 | 営業部 |
| 4 | Mari Takahashi | 2 | 営業部 |
| 5 | Kou Ishida | NULL | NULL |
テーブルの結合③:RIGHT JOIN、RIGHT OUTER JOIN
RIGHT JOINはLEFT JOINのちょうど逆で、右側のテーブルの全てのレコードを取得し、左側のテーブルで結合条件を満たすレコードが存在すればそのデータを結合します。
SELECT employee.id, employee.name, department.dep_id,department.dep_name
FROM employee
RIGHT JOIN department
ON employee.dep_id = department.dep_id;
実行結果は以下のようになります。
| id | name | dep_id | dep_name |
|---|---|---|---|
| 1 | Taro Tanaka | 1 | 人事部 |
| 2 | Hanako Sato | 1 | 人事部 |
| 3 | Ichiro Suzuki | 2 | 営業部 |
| 4 | Mari Takahashi | 2 | 営業部 |
| NULL | NULL | 3 | 開発部 |