sélectionner où deux colonnes sont dans un ensemble

35

C'est peut-être une question idiote, et je soupçonne que je ne peux pas le faire, mais existe-t-il une construction en SQL qui me permettrait de faire quelque chose comme ce qui suit:

SELECT whatever WHERE col1,col2 IN ((val1, val2), (val1, val2), ...)

Je veux sélectionner des données où deux colonnes sont dans un ensemble de paires.

J'aimerais éviter d'utiliser une sous-requête, si possible.

James
la source

Réponses:

49

Y at-il une construction en SQL qui me permettrait de faire quelque chose comme ce qui suit:

Oui, presque, exactement comme vous l'avez écrit. Il suffit de mettre col1, col2entre parenthèses:

-- works in PostgreSQL, Oracle, MySQL, DB2, HSQLDB 
SELECT whatever 
FROM t                               --- you missed the FROM
WHERE (col1, col2)                    --- parentheses here
       IN ((val1a, val2a), (val1b, val2b), ...) ;

Si vous l’essayez cependant dans un SGBD, vous constaterez peut-être que cela ne fonctionne pas. Parce que tous les SGBD n’ont pas implémenté toutes les fonctionnalités du standard SQL (évolutif). Cela fonctionne dans les dernières versions d'Oracle, MySQL, Postgres, DB2 et HSQLDB (il n'était pas bien optimisé dans MySQL et n'utilisait pas d'index, il devrait donc être évité là-bas, à moins que cela ne soit corrigé dans la version 5.7).

Voir la documentation MySQL sur les INopérateurs et la documentation Postgres sur les constructeurs de lignes . Les deux * (ou plus) valeurs entre parenthèses sont appelées constructeurs de lignes .

Autres moyens qui expriment la même idée:

-- works in PostgreSQL, DB2
SELECT whatever 
FROM t 
WHERE (col1, col2) 
       IN ( VALUES (val1a, val2a), (val1b, val2b), ...) ;

SELECT t.whatever 
FROM t 
  JOIN 
    ( VALUES (val1a, val2a), (val1b, val2b), ...) AS x (col1, col2)
      ON (x.col1, x.col2) = (t.col1, t.col2) ;

Les deux travaillent dans Postgres et DB2 (autant que je sache). Le dernier peut également être modifié pour fonctionner dans SQL Server:

-- works in PostgreSQL, DB2, SQL Server
SELECT t.whatever 
FROM t 
  JOIN 
    ( VALUES (val1a, val2a), (val1b, val2b), ...) AS x (col1, col2)
      ON  x.col1 = t.col1
      AND x.col2 = t.col2 ;

Il peut également être modifié pour fonctionner partout, en plaçant d'abord les valeurs dans une table (temporaire ou permanente):

-- works everywhere
CREATE TABLE values_x
( col1  ...,
  col2  ...) ;

-- use appropriate for the DBMS syntax here
INSERT INTO values_x (col1, col2)
VALUES (val1a, val2a), (val1b, val2b), ... ;

SELECT t.whatever 
FROM t 
  JOIN values_x  x 
      ON  x.col1 = t.col1
      AND x.col2 = t.col2 ;

DROP TABLE values_x ;

Et il y a toujours le long chemin à parcourir ou convertir le INà une expression longue avec ORqui devrait fonctionner partout:

-- works in all SQL DBMS
SELECT whatever 
FROM t  
WHERE col1 = val1a AND col2 = val2a
   OR col1 = val1b AND col2 = val2b
   ---
   ;

*: En réalité, il ne peut s'agir que d'une seule valeur, avec ROW(v), voir la documentation Postgres.

ypercubeᵀᴹ
la source
Où puis-je trouver la documentation WHERE (x, y) IN (a,b)? J'utilise MySql. Peut-être que je ne sais pas comment s'appelle cette construction.
Robert Rocha
1
@RobertRocha voir les liens que j'ai ajoutés. C'est ce qu'on appelle un constructeur de lignes: MySQL:IN et Postgres: Constructeurs de lignes
ypercubeᵀᴹ
Il y a aussi WHERE EXISTS (SELECT t.col1, t.col2 [FROM DUAL] INTERSECT VALUES(val1, val2), (…, …), …).
Andriy M
-4
SELECT * 
FROM   dbo.Table1 A
WHERE  (CAST(Column1 AS VARCHAR(max)) + '-' + CAST(Column2 AS varchar(max)))
NOT IN (SELECT (CAST(Column1 AS VARCHAR(max)) 
                + '-' 
                + CAST(Column2 AS varchar(max))) 
        FROM Table2)
Muhammad Zia Ul Islam
la source
2
Cela ne fonctionnera pas de manière fiable
a_horse_with_no_name
2
Oui. Outre l'inefficacité, il ne pourra pas différencier entre 'a-b', 'c'et 'a', 'b-c'. Et cela échouera misérablement pour tous les types qui ne peuvent pas être convertis varchar(max).
ypercubeᵀᴹ