web-dev-qa-db-fra.com

Utilisation des tuples dans la clause SQL "IN"

J'ai une table contenant les champs group_id et group_type et je souhaite interroger la table pour tous les enregistrements possédant un nuplet (id de groupe, type de groupe) à partir d'une liste de tuples. Par exemple, je veux pouvoir faire quelque chose comme:

SELECT *
FROM mytable
WHERE (group_id, group_type) IN (("1234-567", 2), ("4321-765", 3), ("1111-222", 5))

Une question très similaire est déjà posée à l'adresse: utilisation de tuples en sql dans la clause , mais la solution proposée suppose que la liste des Tuple doit être extraite d'une autre table. Cela ne fonctionne pas dans mon cas si les valeurs de Tuple sont codées en dur.

Une solution consiste à utiliser la concaténation de chaînes:

SELECT *
FROM mytable
WHERE group_id + STR(group_type, 1) IN ("1234-5672", "4321-7653", "1111-2225")

Mais le problème est que la table est assez grande et qu'il serait très coûteux d'effectuer une concaténation et une conversion de chaîne pour chaque enregistrement.

Toute suggestion?

41
Rafid

Pourquoi ne pas construire les instructions OR? 

SELECT *
FROM mytable 
WHERE (group_id = '1234-567' and group_type = 2)
    OR (group_id = '4321-765' and group_type = 3)
    OR (group_id = '1111-222' and group_type = 5)

Certes, il n’aura pas l’air aussi beau et élégant que votre exemple de concept, mais il fera l'affaire (et si vous existez avec IN, vous le mettriez en œuvre exactement de la même manière sous les couvertures.

9
Code Magician

Avec un Tweak très mineur (remplacez les guillemets par des guillemets simples et ajoutez le mot clé VALUES), la syntaxe proposée est la syntaxe valide de la norme SQL-92, i.e. 

SELECT *
  FROM mytable
 WHERE (group_id, group_type) IN (
                                  VALUES ('1234-567', 2), 
                                         ('4321-765', 3), 
                                         ('1111-222', 5)
                                 );

Malheureusement, MSFT ne l’a pas ajouté à SQL Server et considère-le comme une fonctionnalité «non planifiée» .

FWIW PostgreSQL et Sqlite sont des exemples de produits SQL prenant en charge cette syntaxe.

43
onedaywhen

Dans SQL Server 2008, vous pouvez procéder comme suit:

select *
from mytable as T
where exists (select *
              from (values ('1234-567', 2), 
                           ('4321-765', 3), 
                           ('1111-222', 5)) as V(group_id, group_type)
              where T.group_id = V.group_id and
                    T.group_type = V.group_type               
             )
23
Mikael Eriksson

Vous pouvez utiliser une expression de table commune pour prétendre que ces n-uplets sont dans une autre table:

;WITH Tuples as (
     select '1234-567' as group_id, 2 as group_type union all
     select '4321-765', 3 union all
     select '1111-222', 5
)
SELECT * /* TODO - Pick appropriate columns */
from mytable m where exists (
   select * from Tuples t
   where m.group_id = t.group_id and m.group_type = t.group_type)
7

En utilisant cette solution, cela devrait fonctionner:

SELECT *
FROM mytable m
WHERE EXISTS (
   SELECT * FROM (
   SELECT "1234-567" group_id, 2 group_type UNION ALL
   SELECT "4321-765", 3 UNION ALL
   SELECT "1111-222", 5) [t]
   WHERE m.group_id = t.group_id AND m.group_type = t.group_type) 

En passant, vous devriez probablement utiliser un CTE pour créer cette table interne.

2
briantyler

Voici une autre solution Tuple utilisant une jointure:

SELECT 
  *
FROM mytable m
JOIN
(
   SELECT "1234-567" group_id, 2 group_type 
   UNION ALL SELECT "4321-765", 3 
   UNION ALL SELECT "1111-222", 5
) [t]
ON m.group_id = t.group_id 
AND m.group_type = t.group_type
0
Lee

J'ai eu un problème similaire, mais ma collection Tuple était dynamique: elle a été envoyée à SQL Server dans un paramètre de requête. Je suis venu avec la solution suivante:

  1. Passer un tuple en XML:

    DECLARE @tuplesXml xml = '<tuples><Tuple group-id="1234-567" group-type="2"/><Tuple group-id="4321-765" group-type="3"/></tuples>';
    
  2. Rejoignez la table que vous souhaitez filtrer avec les nœuds XML:

    SELECT t.* FROM mytable t
    INNER JOIN @tuplesXml.nodes('/tuples/Tuple') AS Tuple(col)
    ON Tuple.col.value('./@group-id', 'varchar(255)') = t.group_id
    AND Tuple.col.value('./@group-type', 'integer') = t.group_type
    

Cela semble bien fonctionner dans mon cas, ce qui est un peu plus complexe que celui décrit dans la question.

N'oubliez pas qu'il est nécessaire d'utiliser t.* au lieu de * et que la table renvoyée par la méthode nodes doit être aliasée (c'est Tuple(col) dans ce cas).

0
Dawid