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?
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.
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.
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
)
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)
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.
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
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:
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>';
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).