47899


MySQLの並び順に関して質問です。以下の様なデータがあるとします。

[code]

    データ1
    id,name,flag
    1,鈴木,0
    2,佐藤,1
    3,佐々木,0
    4,藤木,0
    
    データ2
    id,name,flag
    1,鈴木,0
    2,佐藤,0
    3,佐々木,0
    4,藤木,1
    
[/code]

上記データをFIELD関数以外で以下の様に並び替えてSELECTする方法はありますでしょうか？

[code]

    データ1
    id,name,flag
    2,佐藤,1
    3,佐々木,0
    4,藤木,0
    1,鈴木,0
    
    データ2
    id,name,flag
    4,藤木,1
    1,鈴木,0
    2,佐藤,0
    3,佐々木,0
    
[/code]

以上、宜しくお願い致します。

> > 追加コメント  
>  flagが1なのは必ず1レコードのみ。  
>  レコードの順番は必ず、idが1であれば、次のレコードが1、前のレコードがidの最大値になります。


CC BY-SA 4.0
2018-08-29T01:16:51.590
47897
2018-08-29T09:08:27.463
2018-08-29T02:23:52.733
27919
27919
question
mysql
MySQLでSELECT時の並び順をFIELD以外で並び変える方法


環境無いのでクエリだけ組みました。  
基準となるIDを副問い合わせしてUNIONで結合すればいいのかなぁと  
**追記** ：`UNION`句ではソート順序を保障されません。定数カラムを追加しソートしなおすことが必要です。

[code]

    SELECT *
    FROM (
        SELECT id
             , name
             , flag
             , (SELECT id FROM tableName WHERE flag = 1) AS target
             , 1 AS rank
        FROM tableName
        WHERE target<=id
        UNION ALL
        SELECT id
             , name
             , flag
             , (SELECT id FROM tableName WHERE flag = 1) AS target
             , 2 AS rank
        FROM tableName
        WHERE target>id
    ) a
    ORDER BY rank, id
    
[/code]

* * *

**コメントのやり取りを追記**  
※`UNION`句を使用した場合に`ORDER BY`を使用する場合について  
[Using union and order by clause in
mysql](https://stackoverflow.com/questions/3531251)  
※MySQLリファレンス  
[MySQL 5.6 リファレンスマニュアル :: 13.2.9.4 UNION
構文](https://dev.mysql.com/doc/refman/5.6/ja/union.html)


CC BY-SA 4.0
2018-08-29T02:21:05.050
47899
2018-08-29T05:08:42.093
2018-08-29T05:08:42.093
47897
answer


クエリだけを検証したい人のために、 @Myaku さんの回答を CTE を用いてどこでも利用できるようにすると次です。 MySQL 8.0
以上で実行可能です。

[code]

    with datas(id, name, flag) as (
      select 1, '鈴木' , 0
      union all
      select 2, '佐藤', 1
      union all
      select 3, '佐々木', 0
      union all
      select 4, '藤木', 0
    ), flagged as (
      select * from datas where flag = 1
    ), ranked(id, name, flag, `rank`) as (
      select *, 1 from datas where id >= (select id from flagged)
      union all
      select *, 2 from datas where id < (select id from flagged)
    ) 
    select id, name, flag from ranked order by `rank`, id;
    
[/code]

* * *

## 追記

上を用いて explain をいくつか叩いてみた結果、 MySQL においてはこれが一番良さそうだ、という結論になりました。

[code]

    with
    datas(id, name, flag) as (
      select 1, '鈴木' , 0
      union all
      select 2, '佐藤', 1
      union all
      select 3, '佐々木', 0
      union all
      select 4, '藤木', 0
    )
    select id, name, flag from (
      select datas.*,
             case when id >= (select id from datas where flag = 1)
               then 1
               else 2
             end as `rank`
      from datas 
    ) as ranked
    order by `rank`, id;
    
[/code]

### 理由:

  * subquery (`select id from datas where flag = 1`) の実行を確実に1回に絞ることができる
  * MySQL においては UNION ALL の結果テーブルを一度計算しておかねばならず、なのでテーブルの全走査は少なくとも1回は必要


CC BY-SA 4.0
2018-08-29T06:15:59.923
47904
2018-08-29T09:08:27.463
2018-08-29T09:08:27.463
754
754
47897
answer