MySQL中最有效的方法是从关系表中获取所有关联值,同时对相同的值进行过滤
简化版MySQL中有两个表:一个保存一种person实体,另一个关联表将多个权限与person实体相关联。这些表如下所示: 人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 我现在想要实现的是获得所有的人,包括所有相关的权利,这
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