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にも同様にどのようなオプションがあるのかをメモしておくと便利だと思います。
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()
関数に同じ順序で貼り付けた場合、これはまさにあなたが望むことをします。
ソートされたデータを取得する方法については、以下をご覧ください。
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
思い浮かぶ2つのソリューション:
order by case id when 123 then 1 when 456 then 2 else null end asc
order by instr(','||id||',',',123,456,') asc
(instr()
はOracleのものです。たぶん、locate()
やcharindex()
などがあります)
ソートされたデータを取得するためのAns。
SELECT ...
FROM ...
ORDER BY FIELD(user_id,5,3,2,...,50) LIMIT 10
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で。
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)
本当に素晴らしかった
IN句は値のセットを記述し、セットには順序がありません。
結合を使用してからdisplay_order
列で注文するソリューションは、最も適切なソリューションです。それ以外は、おそらくDBMS固有のハックです(または、標準SQLのOLAP関数を使用して何らかの処理を行っています)。確かに、結合は最も移植性の高いソリューションです(ただし、display_order
値を使用してデータを生成すると問題が発生する可能性があります)。順序列を選択する必要がある場合があることに注意してください。これは標準SQLの要件でしたが、少し前に(おそらくSQL-92ほど前に)ルールとして緩和されたと思いますが。
Oracleでは、instr()関数を使用したジョンのソリューションが機能します。わずかに異なる解決策があります-SELECT id FROM table1 WHERE id IN (1, 20, 45, 60) ORDER BY instr('1, 20, 45, 60', id)
MySQLを使用 FIND_IN_SET 関数:
SELECT *
FROM table_name
WHERE id IN (..,..,..,..)
ORDER BY FIND_IN_SET (coloumn_name, .., .., ..);
最初に考えたのは単一のクエリを書くことでしたが、一方はユーザーによって実行され、他方はバックグラウンドで実行されるため、それは不可能だと言いました。ユーザーからバックグラウンドプロセスに渡すIDのリストをどのように保存しますか?注文を示すための列を持つ一時テーブルにそれらを配置しないのはなぜですか。
これはどうですか:
ハッキングや複雑な処理が行われないように、単に結合を行うだけで完璧にデータを保存できるようにする必要があると思います。
たとえば、SQLiteでは「最近再生した」トラックIDのリストがあります。
SELECT * FROM recently NATURAL JOIN tracks;
私はこれをしようとしましたが、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
複製も許可していることに注意してください。
これを試してください:
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を少し調整する必要がありますが、基本的な原則は正しいはずです。