Мультиключи в SQL WHERE IN
Скажи у тебя 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 ответов
Оба они являются допустимым синтаксисом 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 с различными другими способами сформулировать это:
выбрав, где два столбца находятся в наборе
Если вы используете 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.
Будьте осторожны, как это истолковать. Ответ Акулы сработает, но вернется
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')
Грубым способом было бы объединить 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
В Oracle SQL вы можете просто заменить скобки на фигурные скобки "{}" в исходном посте (второй пример). Может не быть стандартом ANSII, но он близок и работает нормально.
Объединять значения не рекомендуется, даже с необычными разделителями всегда есть небольшой риск неправильного соответствия свободно вводимых текстовых значений. Лучше всего не заводиться.