Мультиключи в SQL WHERE IN - PullRequest
       16

Мультиключи в SQL WHERE IN

3 голосов
/ 19 января 2012

Допустим, у вас есть таблица Accounts, где столбец ID - это PK, а TaxID+AccountNumber - уникальное ограничение:

select * from Accounts where ID in (100, 101)

. Теперь вы хотите сделать аналогичный запрос, используя естественный ключ:

select * from Accounts 
where {TaxID, AccountNumber} in 
  ({"0123456", "2000897"}, {"0125556", "2000866"})

Так что это включает в себя кортежи и выглядит вполне законно.Можно ли как-то выразить с помощью ANSI SQL?Может быть, в каком-то конкретном расширении SQL?Если нет, то почему (буду признателен за любые предположения)?

Ответы [ 5 ]

3 голосов
/ 19 января 2012

Оба из них являются действительным синтаксисом ISO / ANSI Full SQL-92:

SELECT a.* 
FROM Accounts a
  INNER JOIN
    ( VALUES('0123456', '2000897'), ('0125556', '2000866')
    ) AS v(TaxID, AccountNumber) 
  ON (a.TaxID, a.AccountNumber) = (v.TaxID, v.AccountNumber)

SELECT * 
FROM Accounts a
WHERE (a.TaxID, a.AccountNumber) IN 
    ( VALUES ('0123456', '2000897'), ('0125556', '2000866') )

Но я не думаю, что любой из них работает в любой текущей СУБД.


Это также допустимый полный синтаксис SQL-92 (он не работает в SQL-Server 2008 из-за NATURAL JOIN):

SELECT a.* 
FROM Accounts a
  NATURAL JOIN
    ( VALUES('0123456', '2000897'), ('0125556', '2000866')
    ) AS v(TaxID, AccountNumber) 

Это также допустимый SQL (не уверен, если онв спецификации 92 или более поздней версии) - и есть то, что у вас есть (но с использованием скобок, а не фигурных скобок).
Поддерживается MySQL, Postgres, DB2 (но не SQL Server):

SELECT a.* 
FROM Accounts a
WHERE (TaxID, AccountNumber) IN
    ( ('0123456', '2000897'), ('0125556', '2000866') )
  ;

В DBA.SE был похожий вопрос, с различными другими способами сформулировать это:
выбор расположения двух столбцов в наборе

3 голосов
/ 19 января 2012

Если вы используете T-SQL, то вариант, который немного похож на ваш гипотетический запрос, заключается в использовании табличных литералов, например:

select * 
from Accounts a
inner join (values('0123456', '2000897'),('0125556', '2000866')) 
    as v(TaxID, AccountNumber) 
    on a.TaxID = v.TaxID and a.AccountNumber = v.AccountNumber

Здесь вы создаете табличный литерал с именем v, который содержит поля TaxID и AccountNumber. Теперь вы можете объединить литерал таблицы в двух полях, чтобы получить желаемый результат. Одно предостережение заключается в том, что литерал таблицы может содержать только 1000 строк. Вы можете узнать больше о поддержке T-SQL для литералов таблиц на этой странице .

Редактировать: эта страница указывает, что эта конструкция также работает в PostgreSQL.

2 голосов
/ 19 января 2012

Грубым способом было бы объединить 2 значения вместе.

например,

SELECT *
FROM Accounts
WHERE CAST(TaxID AS VARCHAR(10)) + '-' + CAST(AccountNumber AS VARCHAR(10)) 
IN ('0123456-2000897', '......', ....)

Однако, например, в SQL Server это не позволит использовать индекс.

Вы можете добавить вычисляемый столбец, который объединяет оба значения в 1, а затем сопоставить их с этим:

SELECT * FROM Accounts WHERE MyComputedColumn IN ('0123456-2000897', ....)

Или вы можете сделать:

SELECT a.*
FROM Accounts a
    JOIN 
    (
        SELECT '0123456' AS TaxID, '2000897' AS AccountNumber
        UNION ALL
        SELECT '0125556', '2000866'
    ) x ON a.TaxID = x.TaxID AND a.AccountNumber = x.Number
2 голосов
/ 19 января 2012

Будьте осторожны, как это истолковать.Ответ Акулы сработает, но вернет

TaxID        AccountNumber
1234         8765
1234         7654
2345         8765
2345         7654

Что может быть не так, как вы хотите ... Например, если вы хотите только номер счета 8765 для налогового идентификатора 1234 и 7654 для налогового идентификатора 2345, вы бынужно предложение WHERE, подобное этому:

WHERE (taxId'1234' and accountnumber='8765') OR 
      (taxid='2345' and accountNumber='7654')
1 голос
/ 17 октября 2013

В Oracle SQL вы можете просто заменить скобки на фигурные скобки «{}» в исходном сообщении (второй пример).Может не быть стандартом ANSII, но он близок и работает нормально.

Объединять значения не рекомендуется, даже с необычными разделителями всегда есть небольшой риск неправильного соответствия свободно вводимых текстовых значений.Лучше не заводиться.

...