wybranie, gdzie dwie kolumny są w zestawie

35

To może być głupie pytanie i podejrzewam, że nie mogę tego zrobić, ale czy istnieje SQL w konstrukcji, która pozwoliłaby mi zrobić coś takiego:

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

Chcę wybrać dane, w których dwie kolumny są w zestawie par.

Jeśli to możliwe, chciałbym uniknąć korzystania z podzapytania.

James
źródło

Odpowiedzi:

49

Czy istnieje SQL w konstrukcji, która pozwoliłaby mi zrobić coś takiego:

Tak, jest prawie dokładnie tak, jak to napisałeś. Wystarczy umieścić col1, col2w nawiasach:

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

Jeśli jednak spróbujesz tego w DBMS, może się okazać, że to nie działa. Ponieważ nie wszystkie DBMS mają zaimplementowane wszystkie funkcje (ewoluującego) standardu SQL. Działa to w najnowszych wersjach Oracle, MySQL, Postgres, DB2 i HSQLDB (nie zostało dobrze zoptymalizowane w MySQL i nie używa indeksów, więc należy tego unikać, chyba że naprawiono je w 5.7).

Zobacz dokumentację MySQL na temat INoperatora i dokumentację Postgres na temat konstruktorów Row . Dwie * (lub więcej) wartości w nawiasach nazywa się konstruktorem wierszy .

Inne sposoby wyrażania tego samego pomysłu:

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

Oba działają w Postgres i DB2 (afaik). Ten ostatni można również zmodyfikować do pracy w programie 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 ;

Można go również modyfikować, aby działał wszędzie, umieszczając wartości w tabeli (tymczasowej lub stałej) na początku:

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

I zawsze jest długa droga lub konwersja INdługiego wyrażenia, ORktóra powinna działać wszędzie:

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

*: Może to być tylko jedna wartość ROW(v), patrz dokumentacja Postgres.

ypercubeᵀᴹ
źródło
Gdzie mogę znaleźć dokumentację WHERE (x, y) IN (a,b)? Korzystam z MySql. Być może nie wiem, jak się nazywa ten konstrukt.
Robert Rocha,
1
@RobertRocha zobacz linki, które dodałem. Nazywa się to konstruktorem wierszy: MySQL:IN i Postgres: Konstruktory wierszy
ypercubeᵀᴹ
Jest też 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
źródło
2
To nie zadziała niezawodnie
a_horse_w_no_name
2
Tak. Oprócz nieefektywności nie będzie w stanie rozróżnić między 'a-b', 'c'i 'a', 'b-c'. I zawiedzie nieszczęśliwie dla każdego typu, na który nie można przekonwertować varchar(max).
ypercubeᵀᴹ