SQL动态拼接(XML):判断一个列表的元素,是否为数据库的某几个字段的值形成的列表的子集
非常感谢这位佬提供的思路,让我茅塞顿开:sql查询满足任意几个条件的查询方法_sql 满足几个条件-CSDN博客
假设我们有一个表ranks,里面有六个字段,分别是:r1,r2,r3,r4,r5,r6,然后我们的目的是,传入一个List<string> A,并且要求这个列表是r1到r6的值的子集(也就是说,我们把从r1到r6去到的值放到一个List<String> B中,A里面有的元素,B必须要有,但是B有的元素,A可以没有)
举个简单的例子,我们开始查询时,先定位到第一行,然后获取r1到r6字段的值,并且把他放到列表B内,比如['a','b','c','d','e','f'],然后我们传入了一个列表A是:['b','f'];
然后我们判断,A是否为B的子集,也就是A有的元素,B是否都有,如果是,那我们取出,如果不是,就舍弃,这里很明显A是B的子集,因此我们取这行数据;
如果A是['c','g'],那么很明显A就不是B的子集,因此舍弃这行数据。
思路有了,我们就开始code,先来看看完整代码!
1 <select id="inquireOnlyName" resultType="maple.morning.test.entity.Ranks"> 2 SELECT * 3 FROM ranks r 4 WHERE ( 5 CASE WHEN r.r1 IN 6 <foreach collection="names" index="index" item="names" open="(" separator="," close=")"> 7 #{names} 8 </foreach> 9 THEN 1 ELSE 0 END 10 + CASE WHEN r.r2 IN 11 <foreach collection="names" index="index" item="names" open="(" separator="," close=")"> 12 #{names} 13 </foreach> 14 THEN 1 ELSE 0 END 15 + CASE WHEN r.r3 IN 16 <foreach collection="names" index="index" item="names" open="(" separator="," close=")"> 17 #{names} 18 </foreach> 19 THEN 1 ELSE 0 END 20 + CASE WHEN r.r4 IN 21 <foreach collection="names" index="index" item="names" open="(" separator="," close=")"> 22 #{names} 23 </foreach> 24 THEN 1 ELSE 0 END 25 + CASE WHEN r.r5 IN 26 <foreach collection="names" index="index" item="names" open="(" separator="," close=")"> 27 #{names} 28 </foreach> 29 THEN 1 ELSE 0 END 30 + CASE WHEN r.r6 IN 31 <foreach collection="names" index="index" item="names" open="(" separator="," close=")"> 32 #{names} 33 </foreach> 34 THEN 1 ELSE 0 END 35 ) >= #{namesLength} 36 <foreach collection="names" index="index" item="names"> 37 AND #{names} IN ( 38 r.r1 39 r.r2 40 r.r3 41 r.r4 42 r.r5 43 r.r6 44 ) 45 </foreach> 46 </select>
先解释参数:
names:List<String> names,也就是条件列表,就是我们上面提到的A
namesLength:列表长度,也就是上面提到的A的长度
①首先看Where的第一个条件,也就是里面全是case when的那一大段,这一块的条件是获取r1到r6字段中,值在names数组内的数据(我使用了foreach对列表names进行动态传参)
②然后在第一个条件后我加上了namesLength,这是一个字符串占位符,表示列表names的长度
梳理一下,①②的目标就是取出r1到r6中的值,至少有两个字段的值在列表names内(这里很巧妙的判断了数据要满足namesLength个条件才获取);
但是很显然,如果我的数据库中的r1到r6有两个相同的值,例如['g','b','b','c','d','e'],我们想获取含有['a','b']元素的数据,那么这条数据也会被返回,因为出现了两次b,而且我们的列表长度为2,它是符合筛选条件的,然而实际上这条数据我们是不需要的,因此我们要通过下面的步骤再筛选一次!
③接着我们来看namesLength后面那段条件,也就是第二个条件,我们再次循环条件列表names,获取到它的每一个元素,拼接到下面这块代码中:
1 AND #{names} IN ( 2 r.r1 3 r.r2 4 r.r3 5 r.r4 6 r.r5 7 r.r6 8 )
也就是说,names有多少个元素,就会拼接多少个AND条件,然后我们把IN里面的条件列表的值用r1到r6的值填充,即保证每一条数据的r1到r6字段的值,在names的元素中至少有一个匹配,避免了只有①和②出现的情况
如果单独使用③,就会出现下面的情况:
假设我条件列表A为['a','b','b'],我们需要出现的数据的r1到r6中至少有一个元素为a,两个元素为b,但是很显然只有条件③的话,一个元素为a,一个元素为b的数据也会被取出,就不符合我们想要的结果
简而言之就是,我们先获取若干目标字段的值在我们所需的条件范围内,然后再判断这些若干目标字段的值是否包含了我们每一个条件