Использование кортежей в предложении SQL "IN"
У меня есть таблица, содержащая поля group_id и group_type, и я хочу запросить таблицу для всех записей, имеющих любой кортеж (идентификаторгруппы, тип группы) из списка кортежей. Например, я хочу иметь возможность сделать что-то вроде:
SELECT *
FROM mytable
WHERE (group_id, group_type) IN (("1234-567", 2), ("4321-765", 3), ("1111-222", 5))
Очень похожий вопрос уже задавался по адресу: использование кортежей в sql в предложении, но предлагаемое там решение предполагает, что список кортежей должен быть получен из другой таблицы. Это не работает в моем случае, если значения кортежей жестко заданы.
Одним из решений является использование конкатенации строк:
SELECT *
FROM mytable
WHERE group_id + STR(group_type, 1) IN ("1234-5672", "4321-7653", "1111-2225")
Но проблема в том, что таблица довольно большая, и объединение строк и преобразование для каждой записи будет очень дорогим.
Любое предложение?
9 ответов
Почему бы не построить операторы OR?
SELECT *
FROM mytable
WHERE (group_id = '1234-567' and group_type = 2)
OR (group_id = '4321-765' and group_type = 3)
OR (group_id = '1111-222' and group_type = 5)
Конечно, он не выглядит так красиво и аккуратно, как пример вашей концепции, но он справится с работой (и если вы IN
если бы кортежи существовали, то, скорее всего, он реализовал бы их точно так же и под покровами.
Учитывая очень незначительный твик (замените двойные кавычки на одинарные и добавьте VALUES
ключевое слово), предложенный вами синтаксис является действительным стандартным синтаксисом SQL-92, т.е.
SELECT *
FROM mytable
WHERE (group_id, group_type) IN (
VALUES ('1234-567', 2),
('4321-765', 3),
('1111-222', 5)
);
К сожалению, MSFT не добавил его в SQL Server и считает его "незапланированной" функцией.
FWIW PostgreSQL и Sqlite являются примерами продуктов SQL, поддерживающих этот синтаксис.
В SQL Server 2008 вы можете сделать так:
select *
from mytable as T
where exists (select *
from (values ('1234-567', 2),
('4321-765', 3),
('1111-222', 5)) as V(group_id, group_type)
where T.group_id = V.group_id and
T.group_type = V.group_type
)
Вы можете использовать общее табличное выражение, чтобы представить, что эти кортежи находятся в другой таблице:
;WITH Tuples as (
select '1234-567' as group_id, 2 as group_type union all
select '4321-765', 3 union all
select '1111-222', 5
)
SELECT * /* TODO - Pick appropriate columns */
from mytable m where exists (
select * from Tuples t
where m.group_id = t.group_id and m.group_type = t.group_type)
Используя это решение, это должно работать:
SELECT *
FROM mytable m
WHERE EXISTS (
SELECT * FROM (
SELECT "1234-567" group_id, 2 group_type UNION ALL
SELECT "4321-765", 3 UNION ALL
SELECT "1111-222", 5) [t]
WHERE m.group_id = t.group_id AND m.group_type = t.group_type)
Кстати, вам, вероятно, следует использовать CTE для создания этой внутренней таблицы.
Я этого еще не видел, но что-то вроде этого должно работать
SELECT * FROM AgeGroup ag JOIN
(VALUES
('18-24', 18, 24),
('25-34 ', 25, 39),
('35-44 ', 35, 49),
('45-54 ', 45, 59),
('55-64 ', 55, 69),
('65+ ', 65, 299)
) AS x (agegroup, minage, maxage)
ON ag.age_group = x.agegroup
AND ag.min_age=x.minage
AND ag.max_age=x.maxage
У меня была похожая проблема, но моя коллекция кортежей была динамической - она была отправлена на SQL Server в параметре запроса. Я придумал следующее решение:
Передайте кортеж как XML:
DECLARE @tuplesXml xml = '<tuples><tuple group-id="1234-567" group-type="2"/><tuple group-id="4321-765" group-type="3"/></tuples>';
Внутреннее соединение таблицы, которую вы хотите отфильтровать с помощью узлов XML:
SELECT t.* FROM mytable t INNER JOIN @tuplesXml.nodes('/tuples/tuple') AS tuple(col) ON tuple.col.value('./@group-id', 'varchar(255)') = t.group_id AND tuple.col.value('./@group-type', 'integer') = t.group_type
Кажется, в моей ситуации это работает нормально, что немного сложнее, чем описано в вопросе.
Имейте в виду, что необходимо использовать t.*
вместо *
и стол вернулся из nodes
метод должен быть псевдонимом (это tuple(col)
в этом случае).
Вот еще одно решение для кортежей с использованием объединения:
SELECT
*
FROM mytable m
JOIN
(
SELECT "1234-567" group_id, 2 group_type
UNION ALL SELECT "4321-765", 3
UNION ALL SELECT "1111-222", 5
) [t]
ON m.group_id = t.group_id
AND m.group_type = t.group_type
select * from table_name where 1=1 and (column_a, column_b) in ((28,1),(25,1))