SQL动态拼接(XML):判断一个列表的元素,是否为数据库的某几个字段的值形成的列表的子集

发布时间 2023-12-11 17:02:20作者: Morning枫

非常感谢这位佬提供的思路,让我茅塞顿开: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的数据也会被取出,就不符合我们想要的结果


简而言之就是,我们先获取若干目标字段的值在我们所需的条件范围内,然后再判断这些若干目标字段的值是否包含了我们每一个条件