Предложение HAVING с подзапросом - проверка, имеет ли группа хотя бы одно условие соответствия строки - PullRequest
0 голосов
/ 04 февраля 2020

Предположим, у меня есть следующая таблица

DROP TABLE IF EXISTS #toy_example
CREATE TABLE #toy_example
(
    Id int,
    Pet varchar(10)
);
INSERT INTO #toy
VALUES (1, 'dog'),
(1, 'cat'),
(1, 'emu'),
(2, 'cat'),
(2, 'turtle'),
(2, 'lizard'),
(3, 'dog'),
(4, 'elephant'),
(5, 'cat'),
(5, 'emu')

, и я хочу получить все идентификаторы, которые имеют определенных питомцев (например, кошку или эму, поэтому идентификаторы 1, 2 и 5).

DROP TABLE IF EXISTS #Pets
CREATE TABLE #Pets
(
    Animal varchar(10)
);
INSERT INTO #Pets
VALUES ('cat'),
('emu')

SELECT Id
FROM #toy_example
GROUP BY Id
HAVING COUNT(
    CASE
       WHEN Pet IN (SELECT Animal FROM #Pets) 
       THEN 1 
    END
) > 0

Вышеприведенное дает мне ошибку Cannot perform an aggregate function on an expression containing an aggregate or a subquery. У меня есть два вопроса:

  1. Почему это ошибка? Если я вместо этого жестко закодирую подзапрос в предложении HAVING, то есть WHEN Pet IN ('cat','emu'), тогда это работает. Есть ли причина, по которой сервер SQL (я проверял на SQL server 2017 и 2008) не позволяет это сделать?

  2. Что было бы хорошим способом сделать это? Обратите внимание, что выше это просто игрушечный пример. Реальная проблема имеет много возможных «домашних животных», которые я не хочу, чтобы жесткий код. Было бы неплохо, если бы предлагаемый метод мог проверять множественные другие подобные условия в одном запросе.

Ответы [ 2 ]

2 голосов
/ 04 февраля 2020

Если я следил за вами правильно, вы можете просто join и агрегировать:

select t.id, count(*) nb_of_matches
from #toy_example t
inner join #pets p on p.animal = t.pet 
group by t.id

. inner join удаляет записи из #toy_example, которые не совпадают в #pets. Затем мы агрегируем по id и подсчитываем, сколько записей осталось в каждой группе.

Если вы хотите сохранить записи, которые не совпадают в #pets, и отобразить их с количеством 0, то Вы можете left join вместо:

select t.id, count(*) nb_of_records, count(p.animal) nb_of_matches
from #toy_example t
left join #pets p on p.animal = t.pet 
group by t.id
1 голос
/ 04 февраля 2020

Как насчет этого подхода?

SELECT e.Id
FROM #toy_example e JOIN
     #pets p
     ON e.pet = p.animal
GROUP BY e.Id
HAVING COUNT(DISTINCT e.pet) = (SELECT COUNT(*) FROM #pets);
...