web-dev-qa-db-fra.com

Classement par ordre de valeurs dans une clause SQL IN ()

Je me demande s’il est possible de classer par ordre de valeur dans une clause IN ().

Le problème est que j'ai 2 requêtes, une qui obtient tous les identifiants et la seconde qui récupère toutes les informations. Le premier crée l'ordre des ID que je veux que le second commande par ordre. Les ID sont placés dans une clause IN () dans le bon ordre.

Donc, ce serait quelque chose comme (extrêmement simplifié):

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

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

Le problème est que la deuxième requête ne renvoie pas les résultats dans le même ordre que celui où les ID sont placés dans la clause IN ().

Une solution que j'ai trouvée consiste à placer tous les ID dans une table temporaire avec un champ auto-incrémenté qui est ensuite joint à la deuxième requête.

Y a-t-il une meilleure option?

Remarque: La première requête étant exécutée "par l'utilisateur" et la seconde dans un processus en arrière-plan, il est impossible de combiner la requête 2 en 1 à l'aide de sous-requêtes.

J'utilise MySQL, mais je pense qu'il serait peut-être utile de noter également les options disponibles pour les autres bases de données.

141
Darryl Hein

Utilisez la fonction FIELD()) _ de MySQL:

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

FIELD() renverra l'index du premier paramètre égal au premier paramètre (autre que le premier paramètre lui-même).

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

reviendra 1

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

retournera 3

Cela fera exactement ce que vous voulez si vous collez les identifiants dans la clause IN() et la fonction FIELD() dans le même ordre.

175
ʞɔıu

Voir ci-dessous comment obtenir des données triées.

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

Deux solutions qui me viennent à l’esprit:

  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() provient d'Oracle; vous avez peut-être locate() ou charindex() ou quelque chose du genre)

11
John Nilsson

Ans pour obtenir des données triées.

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

Si vous souhaitez effectuer un tri arbitraire sur une requête en utilisant les valeurs entrées par celle-ci dans MS SQL Server 2008 +, vous pouvez le faire en créant une table à la volée et en réalisant une jointure similaire (à l'aide de la nomenclature de l'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

Si vous remplacez l'instruction VALUES par quelque chose d'autre qui fait la même chose, mais en ANSI SQL, cela devrait fonctionner sur n'importe quelle base de données SQL.

Remarque: La deuxième colonne de la table créée (orderTbl.orderIdx) est nécessaire lorsque vous interrogez des jeux d'enregistrements supérieurs à 100 ou plus. À l'origine, je n'avais pas de colonne orderIdx, mais j'ai constaté qu'avec des jeux de résultats supérieurs à 100, je devais trier explicitement cette colonne. dans SQL Server Express 2014 de toute façon.

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)

travaillé vraiment bien

4
Ravi Ranjan

La clause IN décrit un ensemble de valeurs et les ensembles n'ont pas d'ordre.

Votre solution avec une jointure et ensuite commander sur le display_order la colonne est la solution la plus correcte; Tout le reste est probablement un hack spécifique au SGBD (ou fait quelque chose avec les fonctions OLAP en SQL standard). Certes, la jointure est la solution la plus portable (bien que la génération des données avec le display_order les valeurs peuvent être problématiques). Notez que vous devrez peut-être sélectionner les colonnes de classement; Cela était une exigence du SQL standard, même si je pense qu’il a été assoupli il ya quelque temps (peut-être aussi longtemps que SQL-92).

4
Jonathan Leffler

Pour Oracle, la solution de John utilisant la fonction instr () fonctionne. Voici une solution légèrement différente qui a fonctionné - SELECT id FROM table1 WHERE id IN (1, 20, 45, 60) ORDER BY instr('1, 20, 45, 60', id)

2
V Patel

Utilisez MySQL FIND_IN_SET fonction:

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

Ma première pensée a été d'écrire une seule requête, mais vous avez dit que ce n'était pas possible, car l'une est exécutée par l'utilisateur et l'autre en arrière-plan. Comment stockez-vous la liste des identifiants à transmettre de l'utilisateur au processus d'arrière-plan? Pourquoi ne pas les mettre dans une table temporaire avec une colonne pour indiquer l'ordre.

Alors que diriez-vous de ceci:

  1. Le bit d'interface utilisateur s'exécute et insère des valeurs dans une nouvelle table que vous créez. Il insérerait l'identifiant, la position et une sorte d'identifiant de numéro de travail)
  2. Le numéro de travail est passé au processus en arrière-plan (au lieu de tous les identifiants)
  3. Le processus d'arrière-plan effectue une sélection dans la table de l'étape 1 et vous vous connectez pour obtenir les autres informations dont vous avez besoin. Il utilise le numéro de travail dans la clause WHERE et les commandes par la colonne de position.
  4. Une fois l'opération terminée, le processus d'arrière-plan est supprimé de la table en fonction de l'identificateur de travail.
1
WW.

Je pense que vous devriez réussir à stocker vos données de manière à ce que vous fassiez simplement une jointure et que ce soit parfait, donc pas de piratage ni de choses compliquées.

J'ai par exemple une liste "récemment jouée" d'identifiants de piste, sur SQLite je fais simplement:

SELECT * FROM recently NATURAL JOIN tracks;
1
kroe

J'ai juste essayé de faire ceci est MS SQL Server où nous n'avons pas FIELD ():

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

Notez que j'autorise la duplication aussi.

0
Tony

Donnez un coup de feu:

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

Les WHERE nécessiteront probablement quelques ajustements pour que les sous-requêtes corrélées fonctionnent correctement, mais le principe de base doit être valable.

0
chaos