Объединить несколько условий с предложением Имея - PullRequest
0 голосов
/ 17 февраля 2020

Учитывая этого пользователя и таблицу предметов пользователя:

User Table enter image description here

С этим выбором ниже я могу получить кандидатов, у которых есть пункты 3 и 4:

Select 
     U.Id, 
     U.[Name],
     STRING_AGG(UI.ItemId, ',') as Items
from Users U
     Left Join UserItems UI on UI.UserId = U.Id
Where (
     UI.ItemId IN (3, 4)
)
Group By U.Id, U.[Name]
Having Count(*) = 2

Но как я могу получить всех кандидатов, используя больше условий? Например:

UI.ItemId IN (2, 7) OR UI.ItemId IN (3,4) OR UI.ItemId = 2

Есть идеи, как это решить?

Ответы [ 5 ]

4 голосов
/ 17 февраля 2020
SELECT ...
FROM ...
GROUP BY U.Id, U.[Name]
HAVING 2 = COUNT(DISTINCT CASE WHEN UI.ItemId IN (3, 4) THEN UI.ItemId END)
   AND 3 = COUNT(DISTINCT CASE WHEN UI.ItemId IN (5, 6, 7) THEN UI.ItemId END)
   AND ...

UI.ItemId IN (2, 7) ИЛИ UI.ItemId IN (3,4) ИЛИ UI.ItemId = 2

HAVING 2 = COUNT(DISTINCT CASE WHEN UI.ItemId IN (2, 7) THEN UI.ItemId END)
    OR 2 = COUNT(DISTINCT CASE WHEN UI.ItemId IN (3, 4) THEN UI.ItemId END)
    OR 1 = COUNT(DISTINCT CASE WHEN UI.ItemId IN (2) THEN UI.ItemId END)

Если условие означает «одно из 3 перечисленных условий, и нет записей из соответствующего условия», затем измените каждое отдельное на

HAVING (     2 = COUNT(DISTINCT CASE WHEN UI.ItemId IN (2, 7) THEN UI.ItemId END)
         AND 0 = COUNT(DISTINCT CASE WHEN UI.ItemId NOT IN (2, 7) THEN UI.ItemId END)
       )
   OR ...
2 голосов
/ 17 февраля 2020

Во-первых, вы ищете совпадения между двумя таблицами. Используйте JOIN вместо LEFT JOIN.

Вы можете использовать более сложное предложение having:

having (sum(case when ui.itemid = 2 then 1 else 0 end) > 0 and
        sum(case when ui.itemid = 7 then 1 else 0 end) > 0
       ) or
       (sum(case when ui.itemid = 3 then 1 else 0 end) > 0 and
        sum(case when ui.itemid = 4 then 1 else 0 end) > 0
       ) or
       sum(case when ui.itemid = 2 then 1 else 0 end) > 0 

Конечно, первое условие является подмножеством последнего условия так что не надо. Но это иллюстрирует основную идею c.

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

having sum(case when ui.itemid in (2, 7) then 1 else 0 end) = 2 or
       sum(case when ui.itemid in (3, 4) then 1 else 0 end) = 2 or
       sum(case when ui.itemid = 2 then 1 else 0 end) = 1 
0 голосов
/ 17 февраля 2020

Если вы хотите получить всех пользователей с их ItemIds, попробуйте это:

Select 
     U.Id, 
     U.[Name],
     STRING_AGG(UI.ItemId, ',') as Items
from Users U
     left JOIN UserItems UI on UI.UserId = U.Id
Group By U.Id, U.[Name]

Вы должны удалить 'where clause', если вы хотите, чтобы все пользователи. Не используйте «Имеющий предложение» в этом случае

0 голосов
/ 17 февраля 2020
DECLARE @ids TABLE (id INT)
INSERT INTO @ids
VALUES (1), (3)

Select 
     U.Id, 
     U.[Name],
     STRING_AGG(UI.ItemId, ',') as Items
from Users U
     Left Join UserItems UI on UI.UserId = U.Id
     INNER JOIN @ids I on UI.ItemId = I.id
Group By U.Id, U.[Name]
HAVING(COUNT(*) = (SELECT COUNT(*) FROM @ids))
0 голосов
/ 17 февраля 2020

Вы можете использовать агрегирование следующим образом:

select
    u.id,
    u.name,
    string_agg(i.itemId, ',') items
from users u
left join userItems i on i.userId = u.id
group by u.id, u.name
having 
    max(case when i.itemId = 3 then 1 end) = 1
    and max(case when i.itemId = 4 then 1 end) = 1

Вы можете легко расширить предложение having с помощью нескольких комбинаций, например:

having 
    -- users that have both items 3 and 4
    (
        max(case when i.itemId = 3 then 1 end) = 1
        and max(case when i.itemId = 4 then 1 end) = 1
    )
    -- users that have both items 2 and 7
    or (
        max(case when i.itemId = 2 then 1 end) = 1
        and max(case when i.itemId = 7 then 1 end) = 1
   )
    -- users that have items 1
   or max(case when i.itemId = 1 then 1 end) = 1
...