Born Too Late

Yuya's old tech blog.

MySQL で学級委員を選出する... それも全クラスで

2010-01-23 15:36:19

ある学校の、生徒の名前と、その所属クラスを記入した、以下のようなテーブルがあるとします。なお、これらのデータは擬似個人情報データ生成サービスにより出力されたもので、全て架空です。

mysql> SELECT * FROM students;
+-----+---------------------+---------------------------------+---------------------+----------+
| id  | name                | kana                            | roman               | class_id |
+-----+---------------------+---------------------------------+---------------------+----------+
|   1 | 大森 比呂美         | オオモリ ヒロミ                 | Oomori Hiromi       |        1 |
|   2 | 長野 紗矢           | ナガノ サヤ                     | Nagano Saya         |        1 |
|   3 | 杉浦 円             | スギウラ マドカ                 | Sugiura Madoka      |        1 |
~~~
| 198 | 寺本 禎             | テラモト タダシ                 | Teramoto Tadashi    |        5 |
| 199 | 高橋 満雄           | タカハシ ミツオ                 | Takahashi Mitsuo    |        5 |
| 200 | 柴山 謙一           | シバヤマ ケンイチ               | Shibayama Kenichi   |        5 |
+-----+---------------------+---------------------------------+---------------------+----------+
200 rows in set (0.00 sec)

5 クラスあり、どのクラスも 40 名、総勢 200 名の学校です。

さて、それぞれのクラスから 1 名ずつ、学級委員長を選出するとします。当然立候補者はおらず、推薦のなすりつけ合いになることでしょう。そこで、全クラスとも、抽選で学級委員を選出することになりました。

それぞれのクラスでクジを作るのもいい考えですが、我々エンジニアは、MySQL によるソリューションを行うべきでしょう。というわけで以下の SQL を実行。

SELECT
  class_id,
  SUBSTRING_INDEX(GROUP_CONCAT(name ORDER BY RAND()), ',', 1) AS name
FROM
  students
GROUP BY
  class_id

この SQL から得られる結果は以下の通り。

+----------+---------------+
| class_id | name          |
+----------+---------------+
|        1 | 戸田 有沙     |
|        2 | 大森 一行     |
|        3 | 金山 清志     |
|        4 | 藤木 啓一     |
|        5 | 岩渕 雅彦     |
+----------+---------------+
5 rows in set (0.00 sec)

そして、この SQL は、常にランダムな抽出を行うので、当然結果は毎回異なります。例えば以下のように。

+----------+---------------+
| class_id | name          |
+----------+---------------+
|        1 | 高見 幸子     |
|        2 | 森川 里菜     |
|        3 | 塩崎 凛乃     |
|        4 | 高畑 栄三     |
|        5 | 柴山 謙一     |
+----------+---------------+
5 rows in set (0.00 sec)

また、選出された学級委員の名前だけではなく、生徒番号やフリガナ等の情報も紐付ける必要がある場合は、以下のように少々複雑になります。自己結合を使います。

SELECT
  sub.class_id,
  sub.student_id,
  students.name,
  students.kana,
  students.roman
FROM
(
  SELECT
    class_id,
    SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY RAND()), ',', 1) AS student_id
  FROM
    students
  GROUP BY
    class_id
) AS sub
LEFT JOIN
  students
ON
  sub.student_id = students.id
+----------+------------+------------------+------------------------------+-------------------+
| class_id | student_id | name             | kana                         | roman             |
+----------+------------+------------------+------------------------------+-------------------+
|        1 | 32         | 湯川 信明        | ユカワ ノブアキ              | Yukawa Nobuaki    |
|        2 | 78         | 金丸 紗良        | カナマル サラ                | Kanamaru Sara     |
|        3 | 111        | 熊倉 美千子      | クマクラ ミチコ              | Kumakura Michiko  |
|        4 | 150        | 吉田 長次郎      | ヨシダ チョウジロウ          | Yoshida Choujirou |
|        5 | 167        | 日比野 善之      | ヒビノ ヨシユキ              | Hibino Yoshiyuki  |
+----------+------------+------------------+------------------------------+-------------------+
5 rows in set (0.00 sec)

この抽選作業を MySQL で実現する上で、あまり日の当たらない (と、少なくとも私は考えている) 関数が 2 つほど活躍しています。それらについてはまたの機会に紹介しようと思っています。こんな、MySQL クックブックの隅にも載らないような SQL は何人の役に立つかはわかりませんが、GROUP_CONCAT は意外とできる子なので、皆さんも是非使ってみてください。