web-dev-qa-db-ja.com

SQL IN()句の値の順序による順序付け

IN()句内の値の順序で並べ替える方法(おそらくより良い方法)があるかどうか疑問に思っています。

問題は、2つのクエリがあることです。1つはすべてのIDを取得し、もう1つはすべての情報を取得します。 1つ目はIDの順序を作成し、2つ目はその順序を指定します。 IDは正しい順序でIN()句に配置されます。

そのため、次のようなものになります(非常に単純化されています)。

SELECT id FROM table1 WHERE ... ORDER BY display_order, name

SELECT name, description, ... WHERE id IN ([id's from first])

問題は、2番目のクエリが、IDがIN()句に配置されるのと同じ順序で結果を返さないことです。

私が見つけた解決策の1つは、すべてのIDを自動インクリメントフィールドを持つ一時テーブルに入れてから、2番目のクエリに結合することです。

より良いオプションはありますか?

注:最初のクエリは「ユーザーによって」実行され、2番目のクエリはバックグラウンドプロセスで実行されるため、サブクエリを使用して2を1クエリに結合する方法はありません。

私はMySQLを使用していますが、他のDBにも同様にどのようなオプションがあるのか​​をメモしておくと便利だと思います。

141
Darryl Hein

MySQLの FIELD() 関数を使用します:

SELECT name, description, ...
FROM ...
WHERE id IN([ids, any order])
ORDER BY FIELD(id, [ids in order])

FIELD()は、最初のパラメーター(最初のパラメーター自体を除く)と等しい最初のパラメーターのインデックスを返します。

FIELD('a', 'a', 'b', 'c')

1を返します

FIELD('a', 'c', 'b', 'a')

3を返します

IdをIN()句とFIELD()関数に同じ順序で貼り付けた場合、これはまさにあなたが望むことをします。

175
ʞɔıu

ソートされたデータを取得する方法については、以下をご覧ください。

SELECT ...
  FROM ...
 WHERE Zip IN (91709,92886,92807,...,91356)
   AND user.status=1
ORDER 
    BY provider.package_id DESC 
     , FIELD(Zip,91709,92886,92807,...,91356)
LIMIT 10
14
Pradeep Singh

思い浮かぶ2つのソリューション:

  1. order by case id when 123 then 1 when 456 then 2 else null end asc

  2. order by instr(','||id||',',',123,456,') asc

instr()はOracleのものです。たぶん、locate()charindex()などがあります)

11
John Nilsson

ソートされたデータを取得するためのAns。

SELECT ...
FROM ...
ORDER  BY FIELD(user_id,5,3,2,...,50)  LIMIT 10
5

MS SQL Server 2008 +でクエリによって入力された値を使用してクエリで任意の並べ替えを行いたい場合は、その場でテーブルを作成し、そのように結合を行うことで実行できます(命名法を使用) OPから)。

SELECT table1.name, table1.description ... 
FROM (VALUES (id1,1), (id2,2), (id3,3) ...) AS orderTbl(orderKey, orderIdx) 
LEFT JOIN table1 ON orderTbl.orderKey=table1.id
ORDER BY orderTbl.orderIdx

VALUESステートメントを、ANSI SQLで同じことを行う他の何かで置き換えた場合、これはどのSQLデータベースでも動作するはずです。

注:作成されたテーブルの2番目の列(orderTbl.orderIdx)は、100以上のレコードセットをクエリする場合に必要です。元々、orderIdx列はありませんでしたが、結果セットが100を超えると、その列で明示的に並べ替える必要があることがわかりました。とにかくSQL Server Express 2014で。

5
Ian
SELECT ORDER_NO, DELIVERY_ADDRESS 
from IFSAPP.PURCHASE_ORDER_TAB 
where ORDER_NO in ('52000077','52000079','52000167','52000297','52000204','52000409','52000126') 
ORDER BY instr('52000077,52000079,52000167,52000297,52000204,52000409,52000126',ORDER_NO)

本当に素晴らしかった

4
Ravi Ranjan

IN句は値のセットを記述し、セットには順序がありません。

結合を使用してからdisplay_order列で注文するソリューションは、最も適切なソリューションです。それ以外は、おそらくDBMS固有のハックです(または、標準SQLのOLAP関数を使用して何らかの処理を行っています)。確かに、結合は最も移植性の高いソリューションです(ただし、display_order値を使用してデータを生成すると問題が発生する可能性があります)。順序列を選択する必要がある場合があることに注意してください。これは標準SQLの要件でしたが、少し前に(おそらくSQL-92ほど前に)ルールとして緩和されたと思いますが。

4

Oracleでは、instr()関数を使用したジョンのソリューションが機能します。わずかに異なる解決策があります-SELECT id FROM table1 WHERE id IN (1, 20, 45, 60) ORDER BY instr('1, 20, 45, 60', id)

2
V Patel

MySQLを使用 FIND_IN_SET 関数:

  SELECT * 
    FROM table_name 
   WHERE id IN (..,..,..,..) 
ORDER BY FIND_IN_SET (coloumn_name, .., .., ..);
2
Sarthak Sawhney

最初に考えたのは単一のクエリを書くことでしたが、一方はユーザーによって実行され、他方はバックグラウンドで実行されるため、それは不可能だと言いました。ユーザーからバックグラウンドプロセスに渡すIDのリストをどのように保存しますか?注文を示すための列を持つ一時テーブルにそれらを配置しないのはなぜですか。

これはどうですか:

  1. ユーザーインターフェイスビットが実行され、作成した新しいテーブルに値が挿入されます。 ID、位置、ある種のジョブ番号識別子を挿入します)
  2. ジョブ番号は(すべてのIDではなく)バックグラウンドプロセスに渡されます
  3. バックグラウンドプロセスは、手順1でテーブルから選択を行い、必要な他の情報を取得するために参加します。 WHERE句でジョブ番号を使用し、位置列で並べ替えます。
  4. バックグラウンドプロセスが終了すると、ジョブ識別子に基づいてテーブルから削除されます。
1
WW.

ハッキングや複雑な処理が行われないように、単に結合を行うだけで完璧にデータを保存できるようにする必要があると思います。

たとえば、SQLiteでは「最近再生した」トラックIDのリストがあります。

SELECT * FROM recently NATURAL JOIN tracks;
1
kroe

私はこれをしようとしましたが、FIELD()がないMS SQL Serverです:

SELECT table1.id
... 
INNER JOIN
    (VALUES (10,1),(3,2),(4,3),(5,4),(7,5),(8,6),(9,7),(2,8),(6,9),(5,10)
    ) AS X(id,sortorder)
        ON X.id = table1.id
    ORDER BY X.sortorder

複製も許可していることに注意してください。

0
Tony

これを試してください:

SELECT name, description, ...
WHERE id IN
    (SELECT id FROM table1 WHERE...)
ORDER BY
    (SELECT display_order FROM table1 WHERE...),
    (SELECT name FROM table1 WHERE...)

相関サブクエリを適切に機能させるには、WHEREを少し調整する必要がありますが、基本的な原則は正しいはずです。

0
chaos