MySQL中最有效的方法是从关系表中获取所有关联值,同时对相同的值进行过滤

MySQL中最有效的方法是从关系表中获取所有关联值,同时对相同的值进行过滤,mysql,sql,Mysql,Sql,简化版MySQL中有两个表:一个保存一种person实体,另一个关联表将多个权限与person实体相关联。这些表如下所示: 人 person_id | person_name 1 | Michael 2 | Kevin 人2右 person_id | right_id 1 | 1 1 | 2 1 | 4 2 | 1 2 | 2 我现在想要实现的是获得所有的人,包括所有相关的权利,这

简化版MySQL中有两个表:一个保存一种person实体,另一个关联表将多个权限与person实体相关联。这些表如下所示:

person_id | person_name
1         | Michael
2         | Kevin
人2右

person_id | right_id
1         | 1
1         | 2
1         | 4
2         | 1
2         | 2
我现在想要实现的是获得所有的人,包括所有相关的权利,这些权利至少具有定义的权利——在本例中为right_id 1和4

到目前为止,我有一个使用subselect的查询,但我想知道是否有一种更有效的方法可以在没有subselect的情况下实现我的目标,因为MySQL在加入subselect时无法使用索引。我的问题是:

SELECT person_name, GROUP_CONCAT(`person2right`.`right_id`) as `all_rights` 
FROM `person` 
LEFT JOIN `person2right` ON `person`.`person_id` = `person2right`.`person_id` 
LEFT JOIN (
    SELECT `person_id` FROM `person2right` WHERE `right_id` IN (1, 4) 
    GROUP BY `person_id` HAVING COUNT(`person2right`.`right_id`) >= 2
) as `p2r` ON `person`.`person_id` = `p2r`.`person_id` 
WHERE `p2r`.`person_id` IS NOT NULL GROUP BY `person_id`
也许有人有一个不使用子查询的想法。感谢你的帮助


提前谢谢

您可以尝试检查where子句中的权限(避免第二个左连接)


让我们看看附加联接是否可以做到这一点:

select person_name, group_concat(distinct p2r.right_id) as all_rights
from person as p
     inner join person2right as p2r using (person_id) -- You don't need LEFT JOIN, because you'll only return persons with rights
     -- The new stuff starts here: Two new LEFT JOINs to track the rights you want
     left join person2right as p2r_1 using (person_id)
     left join person2right as p2r_4 using (person_id)
where
    -- Here is where you check if the rights exist
    (p2r_1.right_id = 1 and p2r_4.right_id = 4)
group by p.person_id;

查看。

这将仅选择同时拥有权限1和权限4的人员(及其所有相关权限)。请注意,它与您的查询不同之处在于,您的查询选择所有人员(无论其权限如何),并且仅在他们拥有权限1和4时选择其关联的权限

SELECT person_name, GROUP_CONCAT(`person2right`.`right_id`) as `all_rights` 
FROM `person` 
JOIN `person2right` ON `person`.`person_id` = `person2right`.`person_id` 
GROUP BY `person`.`person_id`
HAVING SUM(`right_id` = 4) > 0 AND SUM(`right_id` = 1) > 0
编辑:如果
person2right
中的行是唯一的,则可以将
having
子句更改为

HAVING SUM(`right_id` IN (1,4)) = 2

您是否需要在单个“单元格”中输入
右\u id
值?(组concat()告诉我答案是‘是’)是的。最后的结果必须是在一列中列出所有相关人员的右ID。我认为您的方法非常好。。。当然,现在的问题是:你的查询速度太慢了吗?有几千人的记录,它没有那么慢。但我想知道当有数百万张唱片时会发生什么。我知道在加入子选择时不能使用索引。因此可能存在一个潜在的瓶颈。为什么说在加入子查询时不能使用索引?我找不到好的引用,但却找到了相反的引用:这只是将子查询移动到where条件,不删除在将执行计划与原始OP的查询进行比较时可能注意到的一些重要优势?(我真的很想知道,因为我不习惯分析MySQL执行计划)。。我的解决方案在每个表中使用更少的行来获得相同的结果。当然,性能测试应该使用更大的数据集,这应该是OP的任务。根据我的经验,我发现使用子查询(对于所有问题,除了最简单和最小的问题)都比直接使用索引列慢。如果可以通过过滤索引列(使用
where
)获得结果,那么我更喜欢使用此过滤器而不是使用子查询。但这只是经验和偏好的问题是的,我已经检查了解释,但没有意识到你所显示的差异。谢谢。@Caffé作为第一种方法,如果您看到
使用临时的
,这意味着MySQL必须在某个时候创建一个临时表来执行查询;如果您看到
使用filesort
,这意味着MySQL必须对所有数据进行另一次传递才能返回结果(如果结果很大,则速度很慢)。由于这里没有这种情况,接下来我看到的是:是否使用了索引?每个表正在读取多少行?一个小小的经验法则:将所有行号相乘;使用较小产品的查询通常更好。为什么您认为我的查询会选择所有人,而不考虑他们的权利?我只选择id是子查询结果的人员,子查询只返回具有两个权限的人员id。还是我弄错了?好吧,我测试了你的解决方案。这似乎非常非常有效。只是一个单一的加入是我所寻找的。谢谢。@S-Buzz因为您使用的是左联接(而不是内部联接),所以即使联接条件未满足,您的查询也会从
person
表中选择所有行
HAVING SUM(`right_id` IN (1,4)) = 2