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など)に基づく条件指定を行うことで、柔軟なデータ抽出が可能になります。特に「特定条件を満たすレコードが複数存在するユーザーを探したい」といったケースで、この組み合わせは非常に役立ちます。
-
MySQLでWHERE句に複数の条件を指定してデータを更新する方法
MySQLでWHERE句に複数の値を指定して更新する方法MySQLのUPDATE文では、WHERE句に複数の条件を組み合わせることで、より正確に対象のレコードを絞り込んで更新することができます。AND演算子やOR演算子を使うことで、複数のカラムの値を条件として指定可能です。この記事では、実際にテーブルを作成し、WHERE句に複数の条件を指定してデータを更新する手順を具体的な例とともに解説します。まずはテーブルを作成する最初に、サンプル用のテーブルを作成しましょう。mysql> create table DemoTable -> ( -> Id int, -&
-
MySQLのWHERE句で期日と現在日付のレコードをチェックする条件の書き方
期日(duedate)と現在の日付を比較してレコードの妥当性をチェックしたい場合、MySQLでは IF() 関数 を使うと便利です。NULL値や過去の日付が含まれるデータを自動的に判定し、「Wrong Date」などのラベルを表示させることができます。まずは、動作を確認するためのテーブルを作成しましょう。テーブル作成例mysql> create table demo89 -> ( -> duedate date -> ); Query OK, 0 rows affected (0.78 sec)次に、INSERTコマンドを使ってテーブルにいくつか