Select 个来自 table 的徽章,仅当所有需求行都存在时

Select badges from table only if all requirements rows exist

我得到了三个 table。 如果 badge_requirements table.

badge_id 的所有任务都存在,我想查询徽章 table 以获得徽章

在以下情况下,徽章将被退回,因为对于 badge_id = 1,所有任务都存在。

但是,如果 finished_missions table 中的一条记录不存在,则不会返回徽章。

user_id 将从应用程序提供。

table badges
+----+------+-------+
| id | name | image |
+----+------+-------+
| 1  | OHYE | path  |
+----+------+-------+
PK(id)

table badge_requirements
+------------+----------+
| mission_id | badge_id |
+------------+----------+
| 3          | 1        |
+------------+----------+
| 5          | 1        |
+------------+----------+
UNIQUE(mission_id, badge_id)
FK(mission_id, missions.id)
FK(badge_id, badges.id)

table finished_missions
+----+---------+------------+
| id | user_id | mission_id |
+----+---------+------------+
| 3  | 221     | 3          | // if any of these record doesn't exist
+----+---------+------------+
| 5  | 221     | 5          | // the badge associated with this mission would not be returned
+----+---------+------------+
PK(id)
FK(user_id, users.id)
FK(mission_id, missions.id)

编辑missions table 更改为 finished_missions 以提高可读性。 用户 ID 和任务 ID 仅引用用户和任务 table.

编辑 2 我试过这个,答案是:

SELECT * FROM badges b
INNER JOIN finished_missions fm ON (fm.user_id = 221)
INNER JOIN badge_requirements br ON (br.mission_id = fm.mission_id AND br.badge_id = b.id)

但它仍然是 returns 徽章,即使我在 finished_missions table.

中只有一个记录

一种方法是计数法:

select br.badge_id
from badge_requirements br
group by br.badge_id
having count(distinct mission_id) = (select count(*) from missions);

本returns本badge_id。如果您想了解更多信息,请返回 badges table 或使用 in.

并且,如果 badge_requirements 中没有重复项,则使用 count(*) 而不是 count(distinct)

select * from badges b
inner join mission m on (m.user_id=@userid)
inner join badge_requirements br on (br.mission_id=m.mission_id and br.badge_id=b.id)

其中@userid 是 SQL 参数。

select user_id, badge_id
from
    badge_requirements br on b.id = br.badge_id
    inner join
    missions m on m.id = br.mission_id
group by user_id, badge_id
having
    array_agg(distinct br.mission_id order by br.mission_id) =
    array_agg(distinct m.id order by m.id)
where user_id = 221

方法有很多种。这应该是一个:

SELECT badge_id
FROM  (  -- count missions per badge for the given user
   SELECT br.badge_id, count(*) AS ct
   FROM   finished_missions fm
   JOIN   badge_requirements br USING (mission_id)
   WHERE  fm.user_id = 221
   GROUP  BY  1
   ) u  -- count missions per badge total
JOIN (
   SELECT br.badge_id, count(*) AS ct
   FROM   badge_requirements
   ) b USING (badge_id, ct)  -- only badges with the full count of missions

除了您声明的约束之外,finished_missions 上还应该有一个 UNIQUE(user, mission_id) 来禁止重复条目。或者你必须在第一个子查询中使用count(DISTINCT mission_id) AS ct,所以你可以依赖计数。

UNIQUE(mission_id, badge_id) 确实应该是 PK - 或者为两列添加 NOT NULL 约束。