выбирая, где два столбца находятся в наборе

35

Это может быть глупый вопрос, и я подозреваю, что я не могу этого сделать, но есть ли в SQL конструкция, которая позволила бы мне сделать что-то вроде следующего:

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

Я хочу выбрать данные, где два столбца находятся в наборе пар.

Я хотел бы избежать использования подзапроса, если это возможно.

Джеймс
источник

Ответы:

49

Есть ли в SQL конструкция, которая позволила бы мне сделать что-то вроде следующего:

Да, почти так, как вы это написали. Просто заключите col1, col2в скобки:

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

Однако, если вы попробуете это в СУБД, вы можете обнаружить, что это не работает. Потому что не все СУБД реализовали все функции (развивающегося) стандарта SQL. Это работает в последних версиях Oracle, MySQL, Postgres, DB2 и HSQLDB (это не было хорошо оптимизировано в MySQL и не использовало индексы, поэтому его следует избегать, если они не исправили это в 5.7).

См. Документацию MySQL об INоператорах и документацию Postgres о конструкторах Row . Два * (или более) значения в скобках называются конструктором строк .

Другие способы, которые выражают ту же идею:

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

Оба работают в Postgres и DB2 (afaik). Последний также можно изменить для работы в 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 ;

Его также можно изменить, чтобы он работал везде, сначала поместив значения в (временную или постоянную) таблицу:

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

И всегда есть длинный путь или преобразование в INдлинное выражение, ORкоторое должно работать везде:

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

*: Фактически это может быть только одно значение ROW(v), см. Документацию Postgres.

ypercubeᵀᴹ
источник
Где я могу найти документацию о WHERE (x, y) IN (a,b)? Я использую MySql. Возможно, я не знаю, как называется эта конструкция.
Роберт Роча
1
@RobertRocha смотрите ссылки, которые я добавил. Он называется конструктором строк: MySQL:IN и Postgres: конструкторы
строк
Там тоже WHERE EXISTS (SELECT t.col1, t.col2 [FROM DUAL] INTERSECT VALUES(val1, val2), (…, …), …).
Андрей М
-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)
Мухаммед Зия Ул Ислам
источник
2
Это не будет работать надежно
a_horse_with_no_name
2
Да. Помимо неэффективности, он не сможет различить 'a-b', 'c'и 'a', 'b-c'. И это с треском провалится для любого типа, который не может быть преобразован в varchar(max).
ypercubeᵀᴹ