22
@tsuka816

MySQL - select文でorder by句が正しく効かない場合がある

この記事は最終更新日から5年以上が経過しています。

qiitaのアカウントを作成したものの、これまで全く投稿をしていなかったので、
そろそろ何か投稿しなければと思い立ち、初投稿を試みることにしました。
仕事中にうっかりハマってしまった内容を中心に書いていきたいと思います。

今回のテーマは「MySQL - select文でorder by句が正しく効かない場合がある」というものです。


例えば、MySQLに以下のような定義のテーブルを作成します。

-- メンバーテーブル
CREATE TABLE member (
  member_id int NOT NULL, -- メンバーID
  first_name varchar(45) DEFAULT NULL, -- 名前
  birth_date date DEFAULT NULL, -- 生年月日
  address varchar(45) DEFAULT NULL, -- 住所
  PRIMARY KEY (member_id)
);

このテーブルに以下のようなデータが登録されているとします。

+-----------+------------+------------+----------+
| member_id | first_name | birth_date | address  |
+-----------+------------+------------+----------+
|         1 | Eric       | 1960-10-10 | New York |
|         2 | Pat        | 1959-12-13 | Ohio     |
|         3 | Billy      | 1953-03-19 | America  |
|         4 | Paul       | 1966-11-06 | Illinois |
+-----------+------------+------------+----------+

このテーブルから、member_idの降順でデータを取得してみたいと思います。
通常、以下のselect文のように、order by句でmember_idの降順(desc)を指定するかと思います。

select文①
select
    member_id,
    first_name,
    birth_date,
    address
from 
    member
order by
    member_id desc
;

実行結果は、以下のようになることが予想できると思います。

実行結果①
+-----------+------------+------------+----------+
| member_id | first_name | birth_date | address  |
+-----------+------------+------------+----------+
|         4 | Paul       | 1966-11-06 | Illinois |
|         3 | Billy      | 1953-03-19 | America  |
|         2 | Pat        | 1959-12-13 | Ohio     |
|         1 | Eric       | 1960-10-10 | New York |
+-----------+------------+------------+----------+

ここで、実験をしてみたいと思います。
あえてそれぞれの列に別名を指定してみることにします。

select文②
select 
    member_id as "メンバーID",
    first_name as "名前",
    birth_date as "誕生日",
    address as "住所"
from 
    member 
order by 
   "メンバーID"
desc
;

それぞれの列に日本語で別名を付け、order by句にも別名を指定します。
では、このSQLを実行してみます。すると、以下のような結果となりました。

実行結果②
+------------+-------+------------+----------+
| メンバーID | 名前   | 誕生日     | 住所     |
+------------+-------+------------+----------+
|          1 | Eric  | 1960-10-10 | New York |
|          2 | Pat   | 1959-12-13 | Ohio     |
|          3 | Billy | 1953-03-19 | America  |
|          4 | Paul  | 1966-11-06 | Illinois |
+------------+-------+------------+----------+

???
確かにorder by句は正しく指定されているので、
member_idが4から始まるのを期待していたのですが、
結果はmember_idの降順とはならないようです。

では、以下のselect文ではどうなるか試してみます。

select文③
select 
    member_id as メンバーID,
    first_name as 名前,
    birth_date as 誕生日,
    address as 住所
from 
    member 
order by 
   "メンバーID"
desc

select文②との違いは、
asに続く別名の"(ダブルクォーテーション)を外したという点です。
結果は以下のようになります。

実行結果③
+------------+-------+------------+----------+
| メンバーID | 名前  | 誕生日     | 住所     |
+------------+-------+------------+----------+
|          1 | Eric  | 1960-10-10 | New York |
|          2 | Pat   | 1959-12-13 | Ohio     |
|          3 | Billy | 1953-03-19 | America  |
|          4 | Paul  | 1966-11-06 | Illinois |
+------------+-------+------------+----------+

結果は変わらず、member_idの降順とはなりません。

もうひとつ実験してみたいと思います。

select文④
select 
    member_id as メンバーID,
    first_name as 名前,
    birth_date as 誕生日,
    address as 住所
from 
    member 
order by 
   メンバーID
desc
;

今度は、order by句に指定した別名の"も外しました。
すると、実行結果は以下のようになりました。

実行結果④
+------------+-------+------------+----------+
| メンバーID | 名前  | 誕生日     | 住所     |
+------------+-------+------------+----------+
|          4 | Paul  | 1966-11-06 | Illinois |
|          3 | Billy | 1953-03-19 | America  |
|          2 | Pat   | 1959-12-13 | Ohio     |
|          1 | Eric  | 1960-10-10 | New York |
+------------+-------+------------+----------+

!?
member_idの降順という、期待通りの結果となりました。

もうひとつだけ試してみたいと思います。

select文⑤
select 
    member_id as "メンバーID",
    first_name as "名前",
    birth_date as "誕生日",
    address as "住所"
from 
    member 
order by 
   メンバーID
desc
;
実行結果⑤
+------------+-------+------------+----------+
| メンバーID | 名前  | 誕生日     | 住所     |
+------------+-------+------------+----------+
|          4 | Paul  | 1966-11-06 | Illinois |
|          3 | Billy | 1953-03-19 | America  |
|          2 | Pat   | 1959-12-13 | Ohio     |
|          1 | Eric  | 1960-10-10 | New York |
+------------+-------+------------+----------+

結果から察するに、
order by句に別名を指定する際には、ダブルクォーテーションなしの状態で指定しないと、
正しい順序とはならないようです。
(今回は「メンバーID」をorder by句に指定しましたが、代わりに「誕生日」を指定して試してみても良いかもしれません)

MySQLの仕様なのかどうかは定かではありませんが、
いずれにせよ、SQLを書く時に列の別名に日本語を指定するのはあまり得策ではなさそうです。

22
ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
tsuka816

コメント

(編集済み)

奇妙な動作ですね・・・たぶん次の通りの現象です。

MySQL はダブルクオートで文字列リテラルが書けます。

select "ほげほげ" as str;

MySQL で識別子をクオートするときはバッククオートを使います。

select 'ほげほげ' as `文字列`;

が、なぜか列のエイリアスはダブルクオートやシングルクオートでも大丈夫です。

select 'ほげほげ' as "文字列";
select 'ほげほげ' as '文字列';

なので、下記の SQL の場合・・・

select 
    member_id as "メンバーID",
    first_name as "名前",
    birth_date as "誕生日",
    address as "住所"
from 
    member 
order by 
   "メンバーID"
desc
;

下記と等価なため・・・

select 
    member_id as `メンバーID`,
    first_name as `名前`,
    birth_date as `誕生日`,
    address as `住所`
from 
    member 
order by 
   'メンバーID'
desc
;

member_id as "メンバーID" の メンバーID は列のエイリアスですが、order by "メンバーID" の メンバーID はリテラルとして評価されています。

MySQL 的には次のようにバッククオートを使うのが正しいです。

select 
    member_id as `メンバーID`,
    first_name as `名前`,
    birth_date as `誕生日`,
    address as `住所`
from 
    member 
order by 
   `メンバーID`
desc
;
3

ちなみに SQL_MODE を使えば標準っぽい動作に変更できます。

set session SQL_MODE=ANSI_QUOTES;
select 
    member_id as "メンバーID",
    first_name as "名前",
    birth_date as "誕生日",
    address as "住所"
from 
    member 
order by 
   "メンバーID"
desc
;
2
(編集済み)

ngyuki様
拙い文章を最後まで読んでいただき、また詳細な解説をしてくださり、誠にありがとうございます。

この頃、業務においてSQLを大量に書く機会があり、その中でこの問題に遭遇しました。
試行錯誤の結果、別名にクォーテーションを付けない方法にたどり着きましたが、
日本語がクォーテーションで囲われていないことに若干の違和感が残りました。

ご投稿いただいた内容は、是非とも今後の参考にさせていただきたいと思います。

1
あなたもコメントしてみませんか :)
ユーザー登録
すでにアカウントを持っている方はログイン
記事投稿イベント開催中
フロントエンド強化月間 - 開発する上で知っておくべき知見を共有しよう
~
Azure AIを活用した機械学習に関する記事を投稿しよう!
~