Mysql中多对多关系查询,过滤条件中需要判断是否为子集,通过两次not in等价转换进行优化

文章讨论了一个关于MySQL查询优化的问题,其中涉及两个表和一个中间表来表示多对多关系。开发者面临的问题是如何高效地找出用户基于权限可看到的公告(Announcement)。提出了三种方法:遍历查询、内存构建关系映射和SQL子查询优化。最后的优化方案是通过两次使用NOTIN子查询来排除用户无权限的公告,从而提高查询效率。
摘要由CSDN通过智能技术生成

最近开发过程中遇到了一个mysql查询优化问题。问题简化如下:
假设有两个table, 他们之间是多对多关系。

数据表1:announcement

CREATE TABLE `announcement` (
	`id` int(11) NOT NULL AUTO_INCREMENT,
   ...
   # 其他字段
	PRIMARY KEY (`id`)
);

数据表2:metric

CREATE TABLE `metric` (
	`id` int(11) NOT NULL AUTO_INCREMENT,
	...
	 # 其他字段
	PRIMARY KEY (`id`)

为了记录两个表之间的关系,还需要一个中间表:
中间表: announcement_metric

CREATE TABLE `announcement_metric` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `announcement_id` int(11) NOT NULL,
 `metric_id` int(11) NOT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `announcement_metric` (`announcement_id`,`metric_id`),
 KEY `metric` (metric_id),
 ...
 # 其他字段
) ;

假设每个用户的都有不同的metric权限,对于某个用户的metric权限列表是已知的。如果一个Announcement对应的所有Metric都在用户权限列表中,那么这个Announcment对用户来说是可见的。现在需要得到一个用户所有可见的Announcement列表。
需求比较清晰,可以在代码中比较简单地实现
方法一(遍历记录查询):
遍历announcement中的记录,对每个announcement记录通过中间表查询其对应的metric_id列表,然后判断是否在用户的权限列表中。这种方法最容易想到,不过在迭代过程中用到了多次数据库查询,效率并不高。
方法二(内存中构建announcement和metric的关系map):
对思路一中的方法进行改进,我们可以一次性查询出关系表中的所有记录,构建一个map, key为announcement_id,value为该announcement对应的metric_id列表。然后遍历这个map,记录符合条件的announcement_id。这个方法一次性查询关系表的所有记录,避免了多次查询数据库。不过如果数据量比较大,仍然会耗费我们的内存资源来存储关系表中的数据。

优化查询的一个思路,就是那能否通过编写sql, 将上述逻辑都放在一次数据库查询中,直接在数据库侧查询出结果返回。mysql中并没有现成的函数可以比较直接的实现判断是否是子集的功能,但是我们可以反向思考。只有Announcement对应的所有Metric都在用户权限列表中,这个Announcement对用户来说才是可见的,那么只要Announcement对应的metric,有一个不在用户的权限列表中,那么这个Announcement对用户就是不可见的。排除所有不可见的,就是我们要得到的Announcement列表了。
方法三(直接通过sql查询)

select * from announcement where id not in 
   ( select announcement_id from announcement_metric where metric_id not in ({permission_list}) 

其中{permission_list}代表用户的权限列表。这里通过两次not in实现等价转化,先是在子查询中查出所有不可见的announcement_id,然后再在announcement全集中将这些不可见排除,就是我们想要的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值