selecionando onde duas colunas estão em um conjunto

35

Essa pode ser uma pergunta boba, e minha suspeita é que não posso fazer isso, mas existe uma construção no SQL que me permitiria fazer algo como o seguinte:

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

Eu quero selecionar dados onde duas colunas estão em um conjunto de pares.

Eu gostaria de evitar o uso de uma subconsulta, se possível.

James
fonte

Respostas:

49

Existe uma construção no SQL que me permita fazer algo como o seguinte:

Sim, existe quase exatamente como você o escreveu. Basta colocar col1, col2parênteses:

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

Se você tentar no entanto em um DBMS, poderá achar que ele não funciona. Porque nem todos os DBMS implementaram todos os recursos do padrão SQL (em evolução). Isso funciona nas versões mais recentes do Oracle, MySQL, Postgres, DB2 e HSQLDB (não foi bem otimizado no MySQL e não utiliza índices, portanto, deve ser evitado nesse local, a menos que seja corrigido na versão 5.7).

Veja a documentação do MySQL sobre INoperador e a documentação do Postgres sobre os construtores Row . Os dois valores * (ou mais) entre parênteses são chamados de construtor de linhas .

Outras maneiras que expressam a mesma idéia:

-- 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 trabalham no Postgres e no DB2 (afaik). O último também pode ser modificado para funcionar no 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 ;

Também pode ser modificado para funcionar em qualquer lugar, colocando os valores em uma tabela (temporária ou permanente) primeiro:

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

E sempre há o caminho longo ou a conversão INpara uma expressão longa com ORisso deve funcionar em qualquer lugar:

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

*: Na verdade, pode ser apenas um valor, com ROW(v), consulte os documentos do Postgres.

ypercubeᵀᴹ
fonte
Onde posso encontrar a documentação WHERE (x, y) IN (a,b)? Eu estou usando o MySql. Talvez eu não saiba como essa construção é chamada.
Robert Rocha
1
@RobertRocha veja os links que adicionei. É chamado de um construtor de linha: MySQL:IN e Postgres: Fileira Construtores
ypercubeᵀᴹ
Há também 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
fonte
2
Isso não vai funcionar de forma confiável
a_horse_with_no_name 25/01
2
Sim. Além da ineficiência, não será capaz de diferenciar entre 'a-b', 'c'e 'a', 'b-c'. E falhará miseravelmente em qualquer tipo que não possa ser convertido varchar(max).
precisa saber é o seguinte