Как отфильтровать данные через join таблицу?
Есть таблицы: groups, targets и join таблица groups_targets.
У групп есть таргеты, например:
- у группы №1 есть таргет gender, таргет age, и таргет city.
- у группы №2 есть таргет gender
- у группы №3 есть таргет city и gender.
- у группы №4 есть таргет age и city.
Мой запрос ничего не возвращает
SELECT DISTINCT groups.*
FROM groups
INNER JOIN groups_targets ON groups_targets.group_id = groups.id
INNER JOIN targets ON targets.id = groups_targets.target_id
WHERE groups.user_id = 1
AND (targets.name = 'age' AND targets.name = 'gender')
Как найти группы у которых есть и gender и age, т.е. только группу #1.
Ответы (1 шт):
В вашем запросе условие targets.name = 'age' AND targets.name = 'gender' никогда не может быть выполнено, т.к. применяется к одной строке. Значение этой строки не может быть одновременно равно одному и второму.
Вы можете выполнить свой запрос, объединив условия через OR. В таком случае вернутся группы, где есть либо одно, либо второе, либо оба вместе. После чего, можно сгруппировать по группам и посчитать количество строк. Если число строк равно двум, то это искомый случай - там есть и то и другое.
SELECT g.id
FROM groups AS g
INNER JOIN groups_targets AS gt ON (gt.group_id = g.id)
INNER JOIN targets AS t ON (t.id = gt.target_id )
WHERE g.user_id = 1
AND t.name IN ( 'age', 'gender')
GROUP BY g.id
HAVING COUNT(*) = 2
Данный запрос вернет вам ID групп, в которых оба условия выполняются одновременно.
Второй способ для частного случае, когда ищете 2 условия - два джойна на таблицу связи. Примерно следующим образом:
SELECT g.*
FROM groups AS g
INNER JOIN groups_targets AS gt1 ON (
gt1.group_id = g.id
AND gt1.target_id = (SELECT id FROM tagets WHERE name = 'age')
)
INNER JOIN groups_targets AS gt2 ON (
gt2.group_id = g.id
AND gt2.target_id = (SELECT id FROM tagets WHERE name = 'gender')
)
WHERE g.user_id = 1
Можно и другие вариации придумать с EXISTS либо с джойном производной таблицы, в которую вынести подсчет на равенство двум.
Добавлю еще экзотики c INTERSECT, если вдруг у вас есть индекс в таблице связи на (target_id, group_id) то можно в обратную сторону решить задачу (не знаю по поводу поддержки версий СУБД):
SELECT g.*
FROM groups AS g
INNER JOIN (
SELECT group_id
FROM groups_targets AS gt1
WHERE gt1.target_id = (SELECT id FROM targets WHERE name = 'age')
INTERSECT
SELECT group_id
FROM groups_targets AS gt2
WHERE gt2.target_id = (SELECT id FROM targets WHERE name = 'gender')
) AS ids ON (g.id = ids.group_id)