【MySQL】条件をつけてソートさせる


モニタでこの記事を見つけちゃったみんな―！はじめましてー！
初めての投稿がAdvent Calendar でちょっと緊張しているゆいもっぷだよ！
よろしくね！！


さて、テンションを落ち着かせて早速本題に入ります。

#本題
##テーブルを作ってみようと思います！
###簡単にこんなテーブルを作ります

```
CREATE TABLE `test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
);
```
```
+----+---------+
| id | name    |
+----+---------+
|  1 | Nanako  |
|  2 | Yukari  |
|  3 | Mirai   |
|  4 | Yui     |
|  5 | Miyako  |
|  6 | Nayuta  |
|  7 | Chihiro |
|  8 | Maika   |
|  9 | Mafuyu  |
| 10 | Kaho    |
| 11 | Ginshu  |
| 12 | Ricos   |
| 13 | Yuzu    |
| 14 | Ritsu   |
| 15 | Mio     |
| 16 | Tsumugi |
| 17 | Azusa   |
| 18 | Ui      |
| 19 | Chito   |
| 20 | Yuri    |
+----+---------+
```


##そうなんだ、じゃあ私普通にソートさせるね
###降順でソート
とりあえずidをソートしてみます。

```
> SELECT * FROM test ORDER BY id DESC;

+----+---------+
| id | name    |
+----+---------+
| 20 | Yuri    |
| 19 | Chito   |
| 18 | Ui      |
| 17 | Azusa   |
| 16 | Tsumugi |
| 15 | Mio     |
| 14 | Ritsu   |
| 13 | Yuzu    |
| 12 | Ricos   |
| 11 | Ginshu  |
| 10 | Kaho    |
|  9 | Mafuyu  |
|  8 | Maika   |
|  7 | Chihiro |
|  6 | Nayuta  |
|  5 | Miyako  |
|  4 | Yui     |
|  3 | Mirai   |
|  2 | Yukari  |
|  1 | Nanako  |
+----+---------+
```
うんうん。

###昇順でソート
nameを昇順で並び替えてみます。

```
> SELECT * FROM test ORDER BY name ASC;

+----+---------+
| id | name    |
+----+---------+
| 17 | Azusa   |
|  7 | Chihiro |
| 19 | Chito   |
| 11 | Ginshu  |
| 10 | Kaho    |
|  9 | Mafuyu  |
|  8 | Maika   |
| 15 | Mio     |
|  3 | Mirai   |
|  5 | Miyako  |
|  1 | Nanako  |
|  6 | Nayuta  |
| 12 | Ricos   |
| 14 | Ritsu   |
| 16 | Tsumugi |
| 18 | Ui      |
|  4 | Yui     |
|  2 | Yukari  |
| 20 | Yuri    |
| 13 | Yuzu    |
+----+---------+
```

###複数のカラムをソートする

```
> SELECT * FROM test ORDER BY name DESC, id ASC;
+----+---------+
| id | name    |
+----+---------+
| 13 | Yuzu    |
| 20 | Yuri    |
|  2 | Yukari  |
|  4 | Yui     |
| 18 | Ui      |
| 16 | Tsumugi |
| 14 | Ritsu   |
| 12 | Ricos   |
|  6 | Nayuta  |
|  1 | Nanako  |
|  5 | Miyako  |
|  3 | Mirai   |
| 15 | Mio     |
|  8 | Maika   |
|  9 | Mafuyu  |
| 10 | Kaho    |
| 11 | Ginshu  |
| 19 | Chito   |
|  7 | Chihiro |
| 17 | Azusa   |
+----+---------+
```

ここまではよくみる普通のソートですね。

##条件をつけてソートする
「頭文字にRがつくnameを先頭に持ってきて、かつidを昇順にソートさせる」
といった要望があった時

```
> SELECT * FROM test WHERE name LIKE 'R%'
  UNION SELECT * FROM test WHERE name NOT LIKE 'R%';
+----+---------+
| id | name    |
+----+---------+
| 12 | Ricos   |
| 14 | Ritsu   |
|  1 | Nanako  |
|  2 | Yukari  |
|  3 | Mirai   |
|  4 | Yui     |
|  5 | Miyako  |
|  6 | Nayuta  |
|  7 | Chihiro |
|  8 | Maika   |
|  9 | Mafuyu  |
| 10 | Kaho    |
| 11 | Ginshu  |
| 13 | Yuzu    |
| 15 | Mio     |
| 16 | Tsumugi |
| 17 | Azusa   |
| 18 | Ui      |
| 19 | Chito   |
| 20 | Yuri    |
+----+---------+
```

ぱっと思いつくのがUNIONを使うやり方。

このシンプルなテーブルでシンプルな要望ならいいかもですが、
たとえば「頭文字にU, M, R がつくnameを先頭に持ってきて」(UMAじゃないよ！)
ってなった時に、

```
> SELECT * FROM test WHERE name LIKE 'U%' OR name LIKE 'M%' OR name LIKE 'R%'
UNION SELECT * FROM test WHERE name NOT LIKE 'U%' AND name NOT LIKE 'M%' AND name NOT LIKE 'R%';

+----+---------+
| id | name    |
+----+---------+
|  3 | Mirai   |
|  5 | Miyako  |
|  8 | Maika   |
|  9 | Mafuyu  |
| 12 | Ricos   |
| 14 | Ritsu   |
| 15 | Mio     |
| 18 | Ui      |
|  1 | Nanako  |
|  2 | Yukari  |
|  4 | Yui     |
|  6 | Nayuta  |
|  7 | Chihiro |
| 10 | Kaho    |
| 11 | Ginshu  |
| 13 | Yuzu    |
| 16 | Tsumugi |
| 17 | Azusa   |
| 19 | Chito   |
| 20 | Yuri    |
+----+---------+
```

と、条件が長くなり、また似たような条件が2つあるので
見るだけでゲンナリするものになっちゃいますね。


なので、これをすっきりさせるために
**ORDER BY {条件式}**
と書いてあげます。

こんなかんじ

```
> SELECT * FROM test ORDER BY name LIKE 'U%' OR name LIKE 'M%' OR name LIKE 'R%' DESC, id ASC;
```
だいぶスッキリしました。
ただ、これでもまだ……という感じなので、以下のように変更します。

```
> SELECT * FROM test ORDER BY name REGEXP '^[UMR].*' DESC, id ASC;
```

うん！いいね！

##なぜこういう風にできるのか
これは表示してみるとよくわかると思います。

```
> SELECT *, name REGEXP '^[UMR].*' AS regex FROM test ORDER BY regex DESC, id ASC;

+----+---------+-------+
| id | name    | regex |
+----+---------+-------+
|  3 | Mirai   |     1 |
|  5 | Miyako  |     1 |
|  8 | Maika   |     1 |
|  9 | Mafuyu  |     1 |
| 12 | Ricos   |     1 |
| 14 | Ritsu   |     1 |
| 15 | Mio     |     1 |
| 18 | Ui      |     1 |
|  1 | Nanako  |     0 |
|  2 | Yukari  |     0 |
|  4 | Yui     |     0 |
|  6 | Nayuta  |     0 |
|  7 | Chihiro |     0 |
| 10 | Kaho    |     0 |
| 11 | Ginshu  |     0 |
| 13 | Yuzu    |     0 |
| 16 | Tsumugi |     0 |
| 17 | Azusa   |     0 |
| 19 | Chito   |     0 |
| 20 | Yuri    |     0 |
+----+---------+-------+
```

条件に一致するものは1、 そうでないものは0と処理されるので、
この結果を用いてソートをかけていたということになりますね。

他にもCASE文なども使えるのでこんなソートも可能になります。

```
> SELECT * FROM test ORDER BY CASE WHEN name LIKE 'U%' THEN 3 WHEN name LIKE 'M%' THEN 2 WHEN name LIKE 'R%' THEN 1 ELSE 0 END DESC, id ASC;

+----+---------+------+
| id | name    | sort |
+----+---------+------+
| 18 | Ui      |    3 |
|  3 | Mirai   |    2 |
|  5 | Miyako  |    2 |
|  8 | Maika   |    2 |
|  9 | Mafuyu  |    2 |
| 15 | Mio     |    2 |
| 12 | Ricos   |    1 |
| 14 | Ritsu   |    1 |
|  1 | Nanako  |    0 |
|  2 | Yukari  |    0 |
|  4 | Yui     |    0 |
|  6 | Nayuta  |    0 |
|  7 | Chihiro |    0 |
| 10 | Kaho    |    0 |
| 11 | Ginshu  |    0 |
| 13 | Yuzu    |    0 |
| 16 | Tsumugi |    0 |
| 17 | Azusa   |    0 |
| 19 | Chito   |    0 |
| 20 | Yuri    |    0 |
+----+---------+------+
```

##まとめ
これで色々なソートができるようになりました！
書きたいことを書いてしまってすごく散らかってしまったこの記事も、上手くソートできたらいいんですけどね！