Reihenfolge in der Reihenfolge der Werte in einer SQL IN () -Klausel

154

Ich frage mich, ob es einen Weg gibt (möglicherweise einen besseren Weg), nach der Reihenfolge der Werte in einer IN () -Klausel zu ordnen.

Das Problem ist, dass ich zwei Abfragen habe, eine, die alle IDs erhält, und die zweite, die alle Informationen abruft. Der erste erstellt die Reihenfolge der IDs, nach denen der zweite bestellen soll. Die IDs werden in der richtigen Reihenfolge in eine IN () - Klausel eingefügt.

Es wäre also so etwas wie (extrem vereinfacht):

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

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

Das Problem ist, dass die zweite Abfrage die Ergebnisse nicht in derselben Reihenfolge zurückgibt, in der die IDs in die IN () - Klausel eingefügt wurden.

Eine Lösung, die ich gefunden habe, besteht darin, alle IDs in eine temporäre Tabelle mit einem automatisch inkrementierenden Feld einzufügen, das dann in die zweite Abfrage eingefügt wird.

Gibt es eine bessere Option?

Hinweis: Da die erste Abfrage "vom Benutzer" und die zweite in einem Hintergrundprozess ausgeführt wird, gibt es keine Möglichkeit, die 2-in-1-Abfrage mithilfe von Unterabfragen zu kombinieren.

Ich verwende MySQL, aber ich denke, es könnte nützlich sein, zu notieren, welche Optionen es auch für andere DBs gibt.

Darryl Hein
quelle

Antworten:

186

Verwenden Sie die MySQL- FIELD()Funktion:

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

FIELD() gibt den Index des ersten Parameters zurück, der dem ersten Parameter entspricht (außer dem ersten Parameter selbst).

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

wird 1 zurückgeben

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

wird zurückkehren 3

Dies macht genau das, was Sie wollen, wenn Sie die IDs in derselben Reihenfolge in die IN()Klausel und die FIELD()Funktion einfügen .

ʞɔıu
quelle
3
@Vojto Sortieren nach einer beliebigen Reihenfolge ist per Definition langsam. Dies tut sein Bestes, da es keine Garantie dafür gibt, dass INund FIELDParameter gleich sind. Dies in einem Programmcode zu tun, kann durch Verwendung dieses zusätzlichen Wissens schneller sein. Natürlich könnte es klüger sein, diese Belastung eher auf den Client als auf den Server zu übertragen, wenn Sie die Serverleistung im Auge behalten.
ivan_pozdeev
1
was ist mit sqlite?
fnc12
15

Im Folgenden erfahren Sie, wie Sie sortierte Daten erhalten.

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
Pradeep Singh
quelle
11

Zwei Lösungen, die mir in den Sinn kommen:

  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()ist von Oracle; vielleicht haben Sie locate()oder charindex()oder so etwas)

John Nilsson
quelle
Für MySQL könnte auch FIELD_IN_SET () verwendet werden: dev.mysql.com/doc/refman/5.0/en/…
Darryl Hein
6

Ans, um sortierte Daten zu erhalten.

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

Wenn Sie eine Abfrage willkürlich nach Werten sortieren möchten, die von der Abfrage in MS SQL Server 2008+ eingegeben wurden , können Sie eine Tabelle im laufenden Betrieb erstellen und einen Join wie diesen ausführen (unter Verwendung der Nomenklatur aus 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

Wenn Sie die VALUES-Anweisung durch etwas anderes ersetzen, das dasselbe tut, jedoch in ANSI SQL, sollte dies in jeder SQL-Datenbank funktionieren.

Hinweis: Die zweite Spalte in der erstellten Tabelle (orderTbl.orderIdx) ist erforderlich, wenn Datensatzgruppen abgefragt werden, die größer als 100 sind. Ich hatte ursprünglich keine orderIdx-Spalte, stellte jedoch fest, dass ich bei Ergebnismengen größer als 100 explizit nach dieser Spalte sortieren musste. in SQL Server Express 2014 sowieso.

Ian
quelle
Diese Frage bezieht sich auf MySQL ... nicht sicher, ob Ihre Abfrage in MySQL funktionieren würde.
Darryl Hein
2
@ Darryl, würde es nicht. Dieser Beitrag ist jedoch das beste Ergebnis, wenn Sie bei Google nach der IN-Klausel bestellen. Daher habe ich mir gedacht, dass ich ihn auch hier veröffentlichen kann. Die allgemeine Methodik gilt für alle ANSI-kompatiblen SQL-Server (ersetzen Sie einfach die VALUES-Anweisung durch einen Standard Weg, um eine Tabelle zu erstellen).
Ian
Beachten Sie, dass dies für mich funktioniert hat, aber erst, nachdem ich Verweise auf orderTbl.orderKey, orderTbl.orderIndexdurch orderKey,orderIndex
Peter
5
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)

hat wirklich super funktioniert

Ravi Ranjan
quelle
Arbeitete für mich bei Oracle. Schnell und einfach. Vielen Dank.
Menachem
4

Die IN-Klausel beschreibt eine Reihe von Werten, und Mengen haben keine Reihenfolge.

Ihre Lösung mit einem Join und einer anschließenden Bestellung in der display_orderSpalte ist die nahezu korrekte Lösung. Alles andere ist wahrscheinlich ein DBMS-spezifischer Hack (oder macht einige Dinge mit den OLAP-Funktionen in Standard-SQL). Sicherlich ist der Join die nahezu portable Lösung (obwohl das Generieren der Daten mit den display_orderWerten problematisch sein kann). Beachten Sie, dass Sie möglicherweise die Bestellspalten auswählen müssen. Das war früher eine Anforderung in Standard-SQL, obwohl ich glaube, dass es vor einiger Zeit (vielleicht schon vor SQL-92) in der Regel gelockert wurde.

Jonathan Leffler
quelle
2

Für Oracle funktioniert Johns Lösung mit der Funktion instr (). Hier ist eine etwas andere Lösung, die funktioniert hat - SELECT id FROM table1 WHERE id IN (1, 20, 45, 60) ORDER BY instr('1, 20, 45, 60', id)

V Patel
quelle
2

Verwenden Sie die MySQL- Funktion FIND_IN_SET :

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

Ich habe gerade versucht, dies zu tun, ist MS SQL Server, wo wir kein FIELD () haben:

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

Beachten Sie, dass ich auch Duplikate zulasse.

Tony
quelle
1

Mein erster Gedanke war, eine einzelne Abfrage zu schreiben, aber Sie sagten, dies sei nicht möglich, da eine vom Benutzer und die andere im Hintergrund ausgeführt wird. Wie speichern Sie die Liste der IDs, die vom Benutzer an den Hintergrundprozess übergeben werden sollen? Warum legen Sie sie nicht in eine temporäre Tabelle mit einer Spalte, um die Reihenfolge zu kennzeichnen?

Wie wäre es damit:

  1. Das Benutzeroberflächenbit wird ausgeführt und fügt Werte in eine neue Tabelle ein, die Sie erstellen. Es würde die ID, Position und eine Art Jobnummer-ID einfügen.
  2. Die Auftragsnummer wird an den Hintergrundprozess übergeben (anstelle aller IDs).
  3. Der Hintergrundprozess führt in Schritt 1 eine Auswahl aus der Tabelle durch, und Sie nehmen daran teil, um die anderen erforderlichen Informationen abzurufen. Es verwendet die Jobnummer in der WHERE-Klausel und ordnet nach der Positionsspalte.
  4. Wenn der Hintergrundprozess abgeschlossen ist, wird er basierend auf der Job-ID aus der Tabelle gelöscht.
WW.
quelle
1

Ich denke, Sie sollten es schaffen, Ihre Daten so zu speichern, dass Sie einfach einen Join durchführen und es perfekt ist, damit keine Hacks und komplizierten Dinge passieren.

Ich habe zum Beispiel eine "Kürzlich gespielte" Liste von Track-IDs, auf SQLite mache ich einfach:

SELECT * FROM recently NATURAL JOIN tracks;
kroe
quelle
1
Oh, ich wünschte, das Leben wäre so einfach :)
Darryl Hein
0

Probieren Sie es aus:

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

Die WHEREs werden wahrscheinlich ein wenig angepasst, damit die korrelierten Unterabfragen ordnungsgemäß funktionieren, aber das Grundprinzip sollte solide sein.

Chaos
quelle
siehe Hinweis und wie bereits erwähnt, sind die Abfragebeispiele extrem vereinfacht, sodass ich sie nicht zu einer Abfrage mit Unterabfragen kombinieren kann.
Darryl Hein