MySQL
 Computer >> コンピューター >  >> プログラミング >> MySQL

MySQLでWHERE句とHAVING COUNT(*) > 1を組み合わせたGROUP BYの使い方

はじめに

MySQLでは、GROUP BY句とWHERE句を組み合わせることで、条件に合致するレコードをグループ化し、さらにHAVING句を使ってグループごとの件数を絞り込むことができます。本記事では、実際にテーブルを作成しながら、その具体的な使い方を解説します。

サンプルテーブルの作成

まず、動作確認用のテーブルを作成しましょう。以下のクエリを実行して、テーブル「GroupByWithWhereClause」を作成します。

mysql> create table GroupByWithWhereClause
    -> (
    -> Id int NOT NULL AUTO_INCREMENT,
    -> IsDeleted tinyint(1),
    -> MoneyStatus varchar(20),
    -> UserId int,
    -> PRIMARY KEY(Id)
    -> );
Query OK, 0 rows affected (0.57 sec)

このテーブルは以下の4つのカラムを持っています。

  • Id:自動採番される主キー
  • IsDeleted:削除フラグ(0 = 有効、1 = 削除済み)
  • MoneyStatus:処理状態(done / Undone)
  • UserId:ユーザーID

テストデータの挿入

次に、INSERT文を使ってサンプルデータを登録します。

mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(0,'Undone',101);
Query OK, 1 row affected (0.17 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(0,'done',101);
Query OK, 1 row affected (0.19 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(0,'done',101);
Query OK, 1 row affected (0.14 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(0,'done',102);
Query OK, 1 row affected (0.18 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(1,'Undone',102);
Query OK, 1 row affected (0.20 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(1,'done',102);
Query OK, 1 row affected (0.59 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(0,'Undone',103);
Query OK, 1 row affected (0.15 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(0,'done',103);
Query OK, 1 row affected (0.20 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(0,'done',103);
Query OK, 1 row affected (0.18 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(0,'done',103);
Query OK, 1 row affected (0.10 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(0,'done',104);
Query OK, 1 row affected (0.14 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(0,'Undone',104);
Query OK, 1 row affected (0.12 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(1,'Undone',105);
Query OK, 1 row affected (0.15 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(1,'done',105);
Query OK, 1 row affected (0.26 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(1,'done',105);
Query OK, 1 row affected (0.12 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(0,'done',105);
Query OK, 1 row affected (0.24 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(0,'Undone',106);
Query OK, 1 row affected (0.23 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(0,'done',106);
Query OK, 1 row affected (0.16 sec)
mysql> insert into GroupByWithWhereClause(IsDeleted,MoneyStatus,UserId) values(0,'done',106);
Query OK, 1 row affected (0.14 sec)

登録データの確認

SELECT文ですべてのレコードを表示してみましょう。

mysql> select *from GroupByWithWhereClause;

実行結果は以下の通りです。

+----+-----------+-------------+--------+
| Id | IsDeleted | MoneyStatus | UserId |
+----+-----------+-------------+--------+
|  1 |         0 | Undone      |    101 |
|  2 |         0 | done        |    101 |
|  3 |         0 | done        |    101 |
|  4 |         0 | done        |    102 |
|  5 |         1 | Undone      |    102 |
|  6 |         1 | done        |    102 |
|  7 |         0 | Undone      |    103 |
|  8 |         0 | done        |    103 |
|  9 |         0 | done        |    103 |
| 10 |         0 | done        |    103 |
| 11 |         0 | done        |    104 |
| 12 |         0 | Undone      |    104 |
| 13 |         1 | Undone      |    105 |
| 14 |         1 | done        |    105 |
| 15 |         1 | done        |    105 |
| 16 |         0 | done        |    105 |
| 17 |         0 | Undone      |    106 |
| 18 |         0 | done        |    106 |
| 19 |         0 | done        |    106 |
+----+-----------+-------------+--------+
19 rows in set (0.00 sec)

WHERE句・GROUP BY・HAVINGを組み合わせたクエリ

ここからが本題です。以下のクエリでは、「削除されておらず(IsDeleted=0)、ステータスが'done'であるレコード」をユーザーIDごとにグループ化し、その件数が2件以上(COUNT(*) > 1)のグループのみを抽出しています。

mysql> SELECT * FROM GroupByWithWhereClause
    -> WHERE IsDeleted= 0 AND MoneyStatus= 'done'
    -> GROUP BY SUBSTR(UserId,1,3)
    -> HAVING COUNT(*) > 1
    -> ORDER BY Id DESC;

実行結果は以下の通りです。

+----+-----------+-------------+--------+
| Id | IsDeleted | MoneyStatus | UserId |
+----+-----------+-------------+--------+
| 18 |         0 | done        |    106 |
|  8 |         0 | done        |    103 |
|  2 |         0 | done        |    101 |
+----+-----------+-------------+--------+
3 rows in set (0.00 sec)

クエリのポイント解説

  • WHERE句:グループ化の前にレコードを絞り込みます。ここでは IsDeleted=0 かつ MoneyStatus='done' の行だけが対象になります。
  • GROUP BY SUBSTR(UserId,1,3):UserIdの先頭3文字でグループ化しています。今回のデータでは UserId がすべて3桁のため、実質的にユーザー単位のグループ化と同じ意味になります。
  • HAVING COUNT(*) > 1:グループ化後の各グループに対して件数が2以上のものだけを抽出します。WHERE句が「行単位のフィルタ」であるのに対し、HAVING句は「グループ単位のフィルタ」という点が重要なポイントです。
  • ORDER BY Id DESC:結果をIdの降順で並べ替えています。

まとめ

このように、WHERE句で事前にレコードを絞り込み、GROUP BYでグループ化した後、HAVING句でグループごとの集計結果(COUNTなど)に基づく条件指定を行うことで、柔軟なデータ抽出が可能になります。特に「特定条件を満たすレコードが複数存在するユーザーを探したい」といったケースで、この組み合わせは非常に役立ちます。

  1. MySQLでWHERE句に複数の条件を指定してデータを更新する方法

    MySQLでWHERE句に複数の値を指定して更新する方法MySQLのUPDATE文では、WHERE句に複数の条件を組み合わせることで、より正確に対象のレコードを絞り込んで更新することができます。AND演算子やOR演算子を使うことで、複数のカラムの値を条件として指定可能です。この記事では、実際にテーブルを作成し、WHERE句に複数の条件を指定してデータを更新する手順を具体的な例とともに解説します。まずはテーブルを作成する最初に、サンプル用のテーブルを作成しましょう。mysql> create table DemoTable -> ( -> Id int, -&

  2. MySQLのWHERE句で期日と現在日付のレコードをチェックする条件の書き方

    期日(duedate)と現在の日付を比較してレコードの妥当性をチェックしたい場合、MySQLでは IF() 関数 を使うと便利です。NULL値や過去の日付が含まれるデータを自動的に判定し、「Wrong Date」などのラベルを表示させることができます。まずは、動作を確認するためのテーブルを作成しましょう。テーブル作成例mysql> create table demo89 -> ( -> duedate date -> ); Query OK, 0 rows affected (0.78 sec)次に、INSERTコマンドを使ってテーブルにいくつか