Lesson 6

SELECT文

Lesson 6 Chapter 1
WHERE句、GROUP BY句、HAVING句、ORDER BY句等

SELECT 文はデータベースからデータを取得するための構文です。
これまでのレッスンでも、SELECT 文を使用してきましたが、これからもう少し踏み込んだ内容を学習していきます。

本レッスンでは、SELECT 文で使用する句のうち、代表的な WHERE 句、GROUP BY 句、HAVING 句、ORDER BY 句について順に確認していきます。

テーブルの準備

本レッスンで使用するテーブルを準備します。
PowerShell または TablePlus を使用して、以下のように CREATE TABLE 文、INSERT 文のコマンドを打ち(コピペで良いです)、テーブルを用意してください。

PowerShell の例

以下のようにコマンドを実行します。

PowerShell
mysql> CREATE TABLE members (
  id int primary key,
  name varchar(20),
  age tinyint,
  gender varchar(8),
  prefecture varchar(20)
);

mysql> INSERT INTO members VALUES
  (1, 'tanaka', 28, 'male', 'tokyo'),
  (2, 'yamada', 19, 'female', 'osaka'),
  (3, 'suzuki', 33, 'male', 'tokyo'),
  (4, 'saito', 48, 'male', 'nagoya'),
  (5, 'honda', 31, 'female', 'osaka'),
  (6, 'nakamura', 23, 'male', 'tokyo'),
  (7, 'yamada', 55, 'male', 'fukuoka'),
  (8, 'watanabe', 25, 'male', 'tokyo'),
  (9, 'yamamoto', 41, 'female', 'tokyo'),
  (10, 'kato', 68, 'female', 'fukuoka');

mysql> SELECT * FROM members;
+----+----------+------+--------+------------+
| id | name     | age  | gender | prefecture |
+----+----------+------+--------+------------+
|  1 | tanaka   |   28 | male   | tokyo      |
|  2 | yamada   |   19 | female | osaka      |
|  3 | suzuki   |   33 | male   | tokyo      |
|  4 | saito    |   48 | male   | nagoya     |
|  5 | honda    |   31 | female | osaka      |
|  6 | nakamura |   23 | male   | tokyo      |
|  7 | yamada   |   55 | male   | fukuoka    |
|  8 | watanabe |   25 | male   | tokyo      |
|  9 | yamamoto |   41 | female | tokyo      |
| 10 | kato     |   68 | female | fukuoka    |
+----+----------+------+--------+------------+
10 rows in set (0.00 sec)

ID(id)、名前(name)、年齢(age)、性別(gender)、都道府県(prefecture)をカラムに持った簡単な表ができています。

TablePlus の例

上記の PowerShell の SQL 文を貼り付けます(>mysql は省いてください)。
Run Current の右側のタブから「Run All」を選択すると、表示されている全ての SQL が順に実行されます(もちろん、上から順に「Run Current」を実行しても構いません)。

TablePlus_img01-1

作成されたテーブルを確認してみましょう。
まず、リフレッシュアイコン(下図の赤枠部分)をクリックしてください。
すると、左側に members テーブルが表示されますので、これをクリックします。
無事に成功していれば、作成された表が表示されます。

TablePlus_img01-2

WHERE句

構文

SELECT 文中で WHERE 句を使用することで、取得するレコードの条件を指定することができます。
WHERE 句の基本構文は、次の形式となります。

SELECT [カラム名] FROM [テーブル名] WHERE [抽出条件];

条件に合致するレコードは全て取得します。条件に合致するレコードが無い場合は何も取得しません。

WHERE句の実行

それでは、members テーブルから、「name」が「yamada」のレコードを取得してみます。
次のように、SELECT * FROM members WHERE name = 'yamada'; とコマンドを実行します。

PowerShell
mysql> SELECT * FROM members WHERE name = 'yamada';
+----+--------+------+--------+------------+
| id | name   | age  | gender | prefecture |
+----+--------+------+--------+------------+
|  2 | yamada |   19 | female | osaka      |
|  7 | yamada |   55 | male   | fukuoka    |
+----+--------+------+--------+------------+
2 rows in set (0.00 sec)

WHERE句で使用できる演算子

例えば、次のように、WHERE句内でAND演算子を使用することで、複数条件(A かつ B であること)を指定することができます。

PowerShell
mysql> SELECT * FROM members WHERE name = 'yamada' AND prefecture = 'osaka';
+----+--------+------+--------+------------+
| id | name   | age  | gender | prefecture |
+----+--------+------+--------+------------+
|  2 | yamada |   19 | female | osaka      |
+----+--------+------+--------+------------+
1 row in set (0.00 sec)

このように、WHERE句とともに使用できる演算子として、主に次のものがあります。

No 演算子 説明
1 AND A かつ B の場合(論理積といいます)
2 OR A または B の場合(論理和といいます)
3 IN () 指定したリストに値が含まれている場合
4 BETWEEN ... AND ... 2つの値の範囲内に値が含まれている場合
5 LIKE 文字列のパターンが一致する場合
6 IS 真偽値が一致する場合(カラム名 IS true)
7 IS NOT 真偽値が一致しない場合(カラム名 IS NOT true)
8 IS NULL NULL 値である場合(カラム名 IS NULL)
9 IS NOT NULL NULL 値でない場合(カラム名 IS NOT NULL)

演算子については、次の Lesson 7 で詳細を解説します。
ここでは、WHERE 句と代表的な演算子を確認しておきましょう。

AND / OR 演算子(複数条件の指定)

条件を複数指定する場合は AND、OR 演算子を使用します。
AND の場合は「A かつ B」、OR の場合は「A または B」という条件指定となります。
基本構文は次のとおりです。

SELECT (カラム名) FROM (テーブル名) WHERE (条件1) (OR | AND) (条件2);

それでは prefecture が osaka または fukuoka のレコードを全て取得してみましょう。 SQL 文は次のように OR 演算子を使用して指定を行います。

SELECT * FROM members WHERE prefecture = 'osaka' OR prefecture = 'fukuoka';

実行すると次のような結果になります。

PowerShell
mysql> SELECT * FROM members WHERE prefecture = 'osaka' OR prefecture = 'fukuoka';
+----+--------+------+--------+------------+
| id | name   | age  | gender | prefecture |
+----+--------+------+--------+------------+
|  2 | yamada |   19 | female | osaka      |
|  5 | honda  |   31 | female | osaka      |
|  7 | yamada |   55 | male   | fukuoka    |
| 10 | kato   |   68 | female | fukuoka    |
+----+--------+------+--------+------------+
4 rows in set (0.00 sec)

prefecture が osaka または fukuoka のレコードを全て取得できました。

複数条件の指定方法

① 間違った例

次のように、WHERE 句を WHERE prefecture = 'osaka' OR 'fukuoka' と指定してしまうと、正しい結果を得ることができません。

× 間違った例
SELECT * FROM members WHERE prefecture = 'osaka' OR 'fukuoka';

これは、「prefecture = 'osaka' または 'fukuoka'」 とコンピュータが解釈するため、後者の 'fukuoka' は条件として単独では成り立たないことになります。

② 正しい例

正しくは、「prefecture = 'osaka' または prefecture = 'fukuoka'」 という形で指定して、コンピュータに解釈させる必要があります。

○ 正しい例
SELECT * FROM members WHERE prefecture = 'osaka' OR prefecture = 'fukuoka';

IN 演算子(リストによる指定)

次に、IN 演算子についてです。この演算子を使用することで、指定したリスト(配列)のうちで一致する値があるレコードを取得することができます。
基本構文は次のとおりです。

SELECT [カラム名] FROM [テーブル名] WHERE [カラム名] IN (条件1, 条件2, ...);

「id」カラムの値が 2, 4 の場合のレコードを取得するには、次のようにSQL文を指定して実行します。

PowerShell
mysql> SELECT * FROM members WHERE id IN (2, 4);
+----+--------+------+--------+------------+
| id | name   | age  | gender | prefecture |
+----+--------+------+--------+------------+
|  2 | yamada |   19 | female | osaka      |
|  4 | saito  |   48 | male   | nagoya     |
+----+--------+------+--------+------------+
2 rows in set (0.00 sec)

BETWEEN 演算子(範囲による指定)

BETWEEN 演算子は、指定した範囲内に値があるレコードを取得します。
基本構文は次のとおりです。

SELECT [カラム名] FROM [テーブル名] WHERE [カラム名] BETWEEN [最小値] AND [最大値];

年齢(age)の値が 28 から 33 の間のレコードを取得する場合は、次のように SQL 文を指定します。

PowerShell
mysql> SELECT * FROM members WHERE age BETWEEN 28 AND 33;
+----+--------+------+--------+------------+
| id | name   | age  | gender | prefecture |
+----+--------+------+--------+------------+
|  1 | tanaka |   28 | male   | tokyo      |
|  3 | suzuki |   33 | male   | tokyo      |
|  5 | honda  |   31 | female | osaka      |
+----+--------+------+--------+------------+
3 rows in set (0.00 sec)

上記の結果のように、最小値として指定した 28、最大値として指定した 33 のレコードも含まれています。
これは、SELECT * FROM members WHERE age >= 28 AND age と指定した場合と同義になります。

LIKE 演算子(パターンマッチング)

LIKE 演算子を使用すると、指定した文字列と部分一致するレコードを取得することができます。
基本構文は次のとおりです。

SELECT [カラム名] FROM [テーブル名] WHERE [カラム名] LIKE [パターン];

実例を見た方が分かりやすいと思います。これは、name が s で始まるレコードを取得する場合です。

PowerShell
mysql> SELECT * FROM members WHERE name LIKE 's%';
+----+--------+------+--------+------------+
| id | name   | age  | gender | prefecture |
+----+--------+------+--------+------------+
|  3 | suzuki |   33 | male   | tokyo      |
|  4 | saito  |   48 | male   | nagoya     |
+----+--------+------+--------+------------+
2 rows in set (0.00 sec)

上記で指定した「%」は、任意の 0 文字以上の文字列を表すワイルドカードとなります。

LIKE 演算子で使用できるワイルドカード文字は次の2つです。

No ワイルドカード文字 説明
1 % (半角のパーセント) 0 個以上の任意の文字に一致
2 _ (半角のアンダースコア) 任意の 1 つの文字に一致

GROUP BY 句

GROUP BY 句を使用することで、指定したカラムの値に基づきレコードをグループ化することができます。
具体例を見た方が分かりやすいので、WHERE 句で使用した次のテーブルを引き続き使用して、SQL を実行していきましょう。

+----+----------+------+--------+------------+
| id | name     | age  | gender | prefecture |
+----+----------+------+--------+------------+
|  1 | tanaka   |   28 | male   | tokyo      |
|  2 | yamada   |   19 | female | osaka      |
|  3 | suzuki   |   33 | male   | tokyo      |
|  4 | saito    |   48 | male   | nagoya     |
|  5 | honda    |   31 | female | osaka      |
|  6 | nakamura |   23 | male   | tokyo      |
|  7 | yamada   |   55 | male   | fukuoka    |
|  8 | watanabe |   25 | male   | tokyo      |
|  9 | yamamoto |   41 | female | tokyo      |
| 10 | kato     |   68 | female | fukuoka    |
+----+----------+------+--------+------------+

構文

GROUP BY 句の基本構文は、次の形式となります。

SELECT [カラム名] FROM [テーブル名] GROUP BY [グループ化カラム名];

なお、GROUP BY 句を使用すると、SELECT 句の次に指定する [カラム名] には制限が生じます。
この [カラム名] に指定できるのは、原則として、次の2つになります。

① GROUP BY 句で指定する [グループ化カラム名]
② 集合関数(※)

集合関数については、Lesson 9 で改めて紹介しますが、主に次のようなものがあります。
No 関数 説明
1 COUNT() 引数に指定したカラムの値が存在するレコード数をカウントします
2 MAX() 引数に指定したカラムの最大値を取得します
3 MIN() 引数に指定したカラムの最小値を取得します
4 SUM() 引数に指定したカラムの合計値を取得します
5 AVG() 引数に指定したカラムの平均値を取得します

SQL 文の指定

ここでは、prefecture でグループ化して、それぞれのレコード数も算出するようにしてみます。
SQL 文は次のようになります。

SELECT COUNT(*), prefecture FROM members GROUP BY prefecture;

カラム名の指定に、COUNT(*) という集合関数が出てきています。
引数にカラム名でなく * を入れると、そのグループに含まれる全レコード数が取得されます。
実行すると次のような結果となります。

PowerShell
mysql> SELECT COUNT(*), prefecture FROM members GROUP BY prefecture;
+----------+------------+
| COUNT(*) | prefecture |
+----------+------------+
|        2 | fukuoka    |
|        1 | nagoya     |
|        2 | osaka      |
|        5 | tokyo      |
+----------+------------+
4 rows in set (0.00 sec)

各都道府県のレコード数が、正しく取得できていることが確認できました。

複数のカラムを GROUP BY 句に指定する

次に、GROUP BY 句 に複数のカラムを指定する場合です。
ここでは、genderprefecture でグループ化して、それぞれのレコード数を算出してみます。
SQL 文は次のようになります。

SELECT COUNT(*), gender, prefecture FROM members GROUP BY gender, prefecture;

実行すると次のような結果となります。

PowerShell
mysql> SELECT COUNT(*), gender, prefecture FROM members GROUP BY gender, prefecture;
+----------+--------+------------+
| COUNT(*) | gender | prefecture |
+----------+--------+------------+
|        1 | female | fukuoka    |
|        2 | female | osaka      |
|        1 | female | tokyo      |
|        1 | male   | fukuoka    |
|        1 | male   | nagoya     |
|        4 | male   | tokyo      |
+----------+--------+------------+
6 rows in set (0.00 sec)

GROUP BY 句 にカラムを複数指定しても、正しく実行できることが確認できました。

HAVING 句

HAVING 句は WHERE 句と近しい役割を持つ

HAVING 句は、WHERE 句と同じように、取得するレコードの条件を指定する際に使用します。(※ただし、後述するように、原則として GROUP BY 句とセットで使用します。)

本来の使い方ではないですが、次のように WHERE 句のように使用することもできてしまいます。

PowerShell(参考)
mysql> SELECT * FROM members HAVING name = 'yamada';
+----+--------+------+--------+------------+
| id | name   | age  | gender | prefecture |
+----+--------+------+--------+------------+
|  2 | yamada |   19 | female | osaka      |
|  7 | yamada |   55 | male   | fukuoka    |
+----+--------+------+--------+------------+
2 rows in set (0.00 sec)

HAVING 句と WHERE 句の違い

それでは、HAVING 句と WHERE 句の違いは何でしょうか。
答えは、以下の表のように、SELECT 文の中で実行される順番が異なることにあります。

SELECT 文における処理順序
実行順 項目
1 FROM
2 JOIN
3 WHERE
4 GROUP BY
5 HAVING
6 SELECT
7 DISTINCT
8 ORDER BY
9 LIMIT

つまり、GROUP BY 句で指定した内容は、その後に実行される「HAVING 句」でしか絞込みができないということになります。

構文

HAVING 句の基本構文は、次の形式となります。

SELECT [カラム名] FROM [テーブル名] GROUP BY [カラム名] HAVING [抽出条件];

HAVING 句の実行

それでは、引き続き次のテーブルを使用して、実際に SQL を実行していきます。

+----+----------+------+--------+------------+
| id | name     | age  | gender | prefecture |
+----+----------+------+--------+------------+
|  1 | tanaka   |   28 | male   | tokyo      |
|  2 | yamada   |   19 | female | osaka      |
|  3 | suzuki   |   33 | male   | tokyo      |
|  4 | saito    |   48 | male   | nagoya     |
|  5 | honda    |   31 | female | osaka      |
|  6 | nakamura |   23 | male   | tokyo      |
|  7 | yamada   |   55 | male   | fukuoka    |
|  8 | watanabe |   25 | male   | tokyo      |
|  9 | yamamoto |   41 | female | tokyo      |
| 10 | kato     |   68 | female | fukuoka    |
+----+----------+------+--------+------------+

まず、prefecture でグループ化した上で、レコード数が 2 以上のデータを取得するようにしてみます。
SQL 文は次のようになります。

SELECT COUNT(*), prefecture FROM members GROUP BY prefecture HAVING COUNT(*) >= 2;

実行すると次のような結果となります。

PowerShell
mysql> SELECT COUNT(*), prefecture FROM members GROUP BY prefecture HAVING COUNT(*) >= 2;
+----------+------------+
| COUNT(*) | prefecture |
+----------+------------+
|        2 | fukuoka    |
|        2 | osaka      |
|        5 | tokyo      |
+----------+------------+
3 rows in set (0.00 sec)

レコード数が 1 しかない nagoya を除いて、レコード数が 2 以上のデータを取得することができました。

WHERE 句と HAVING 句の双方を使用する SQL 文

以上で見てきましたように、WHERE 句と HAVING 句の役割は異なります。そのため、一つの SQL 文の中で WHERE 句と HAVING 句の双方を使用することもあります。
ここでは、先述の「SELECT 文における処理順序」の表を念頭に置きつつ、次のような SQL 文を考えてみます。

① まず、WHERE 句でレーコードを絞り込む
② 絞込み後のレコードを GROUP BY 句でグループ化する
③ グループ化されたデータに対して、HAVING 句で更に絞込みを行う

具体的には、次の条件で SQL を作成してみます。

① WHERE 句で age が 30 歳以上であるレコードを抽出する。
② GROUP BY 句で prefecture でグループ化を行う。
③ HAVING 句で COUNT(*) が 2 以上のレコードのみに絞り込む。

使用する構文は、次のようになります。

SELECT [カラム名] FROM [テーブル名] WHERE [抽出条件] GROUP BY [カラム名] HAVING [抽出条件];

実際に SQL 文を作成すると、次のようになります。
SQL 文が長くなるため、句ごとに折り返しを行っています。

SELECT COUNT(*), prefecture
  FROM members
  WHERE age >= 30
  GROUP BY prefecture
  HAVING COUNT(*) >= 2;

実行結果は次のようになります。

mysql> SELECT COUNT(*), prefecture FROM members WHERE age >= 30 GROUP BY prefecture HAVING COUNT(*) >= 2;
+----------+------------+
| COUNT(*) | prefecture |
+----------+------------+
|        2 | fukuoka    |
|        2 | tokyo      |
+----------+------------+
2 rows in set (0.00 sec)

期待した結果を取得することができました。
HAVING 句を理解するポイントは、処理の実行順序を把握することです。
結果として「GROUP BY 句とセットで使用する」ということになります。

ORDER BY 句

ORDER BY 句を使用することで、取得データの並べ替え(ソート)をすることができます。

構文

SQL 文では、① 並べ替え(ソート)を行うカラムを指定し、② 昇順か降順を指定することになります。
ORDER BY 句の基本構文は、次のとおりです。

SELECT [カラム名] FROM [テーブル名] ORDER BY [ソート対象のカラム名] [ASC | DESC];

末尾のオプション [ASC | DESC] で並べ替え順(ソート順)の指定をします。

No 項目 説明
1 ASC 昇順(ascending order)を指定。デフォルト値。
2 DESC 降順(descending order)を指定。

デフォルトが ASC のため、指定しない場合は、昇順になります。

ORDER BY 句の実行

それでは、引き続き次のテーブルを使用して、ORDER BY 句 を使用してみましょう。

+----+----------+------+--------+------------+
| id | name     | age  | gender | prefecture |
+----+----------+------+--------+------------+
|  1 | tanaka   |   28 | male   | tokyo      |
|  2 | yamada   |   19 | female | osaka      |
|  3 | suzuki   |   33 | male   | tokyo      |
|  4 | saito    |   48 | male   | nagoya     |
|  5 | honda    |   31 | female | osaka      |
|  6 | nakamura |   23 | male   | tokyo      |
|  7 | yamada   |   55 | male   | fukuoka    |
|  8 | watanabe |   25 | male   | tokyo      |
|  9 | yamamoto |   41 | female | tokyo      |
| 10 | kato     |   68 | female | fukuoka    |
+----+----------+------+--------+------------+

ここでは age(年齢)順に並べ替え(ソート)を実行してみます。
SQL 文は次のようになります。
並べ替え順(ASC、DESC)の指定をしていないので、デフォルトの昇順になるはずです。

SELECT * FROM members ORDER BY age;

実行結果は次のようになります。

mysql> SELECT * FROM members ORDER BY age;
+----+----------+------+--------+------------+
| id | name     | age  | gender | prefecture |
+----+----------+------+--------+------------+
|  2 | yamada   |   19 | female | osaka      |
|  6 | nakamura |   23 | male   | tokyo      |
|  8 | watanabe |   25 | male   | tokyo      |
|  1 | tanaka   |   28 | male   | tokyo      |
|  5 | honda    |   31 | female | osaka      |
|  3 | suzuki   |   33 | male   | tokyo      |
|  9 | yamamoto |   41 | female | tokyo      |
|  4 | saito    |   48 | male   | nagoya     |
|  7 | yamada   |   55 | male   | fukuoka    |
| 10 | kato     |   68 | female | fukuoka    |
+----+----------+------+--------+------------+
10 rows in set (0.02 sec)

age カラムを昇順に並び変えた形で取得できました。

降順に並べ替える

次に、降順で ORDER BY 句 を使用してみましょう。
併せて WHERE 句も使用して、prefecture = 'tokyo' のレコードに限定するようにしてみます。
SQL 文は次のようになります。

SELECT * FROM members WHERE prefecture = 'tokyo' ORDER BY age DESC;

実行結果は次のようになります。

mysql> SELECT * FROM members WHERE prefecture = 'tokyo' ORDER BY age DESC;
+----+----------+------+--------+------------+
| id | name     | age  | gender | prefecture |
+----+----------+------+--------+------------+
|  9 | yamamoto |   41 | female | tokyo      |
|  3 | suzuki   |   33 | male   | tokyo      |
|  1 | tanaka   |   28 | male   | tokyo      |
|  8 | watanabe |   25 | male   | tokyo      |
|  6 | nakamura |   23 | male   | tokyo      |
+----+----------+------+--------+------------+
5 rows in set (0.00 sec)

prefecture = 'tokyo' に絞込みを行った上で age カラムを降順で取得できました。

SELECT 文における「句」の記載順序

先ほどの例で、WHERE 句と ORDER BY 句の併用を行いました。
実行した SQL 文では WHERE 句を先に、ORDER BY 句を後に記述しましたが、これには決まった順番が存在しています。
記載順序は次のとおりなので、ぜひ覚えておきましょう。

SELECT 文における記載順序
記載順 項目
1 SELECT
2 FROM
3 WHERE
4 GROUP BY
5 HAVING
6 ORDER BY
7 LIMIT

本レッスンは以上です。