seleccionar dónde están dos columnas en un conjunto

35

Esta podría ser una pregunta tonta, y mi sospecha es que no puedo hacer esto, pero ¿hay una construcción en SQL que me permita hacer algo como lo siguiente:

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

Quiero seleccionar datos donde dos columnas están en un conjunto de pares.

Me gustaría evitar usar una subconsulta, si es posible.

James
fuente

Respuestas:

49

¿Hay una construcción en SQL que me permita hacer algo como lo siguiente:

Sí, la hay, casi exactamente como la escribiste. Solo ponga col1, col2entre paréntesis:

-- 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), ...) ;

Sin embargo, si lo prueba en un DBMS, es posible que no funcione. Porque no todos los DBMS han implementado todas las características del estándar SQL (en evolución). Esto funciona en las últimas versiones de Oracle, MySQL, Postgres, DB2 y HSQLDB (no estaba bien optimizado en MySQL y no usaba índices, por lo que debería evitarse allí a menos que lo arreglaran en 5.7).

Consulte la documentación de MySQL sobre el INoperador y la documentación de Postgres sobre los constructores de filas . Los dos valores * (o más) entre paréntesis se llaman constructores de filas .

Otras formas que expresan la misma idea:

-- 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) ;

Ambos funcionan en Postgres y DB2 (afaik). El último también se puede modificar para que funcione en 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 ;

También se puede modificar para que funcione en todas partes, colocando primero los valores en una tabla (temporal o 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 ;

Y siempre hay un largo camino o convertirlo INen una expresión larga con OReso debería funcionar en todas partes:

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

*: En realidad puede ser solo un valor, con ROW(v), ver documentos de Postgres.

ypercubeᵀᴹ
fuente
¿Dónde puedo encontrar la documentación WHERE (x, y) IN (a,b)? Estoy usando MySql. Quizás no sé cómo se llama esta construcción.
Robert Rocha
1
@RobertRocha ver los enlaces que agregué. Se llama un constructor de filas: MySQL:IN y Postgres: Constructores de filas
ypercubeᵀᴹ
También hay 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
fuente
2
Eso no va a funcionar de manera confiable
a_horse_with_no_name
2
Sí. Además de la ineficiencia, no podrá diferenciar entre 'a-b', 'c'y 'a', 'b-c'. Y fallará miserablemente para cualquier tipo al que no se pueda convertir varchar(max).
ypercubeᵀᴹ