现有学生表、课程表、学生-课程关系表,学生与课程为多对多关系。前端需要分页查询接口来展示学生信息与对应的多个课程信息,并且要对课程名称进行搜索。信息及展示效果如下:
学生姓名 | 课程名 |
---|---|
小明 | 高等数学、大学物理 |
小张 | 线性代数、概率论与数理统计 |
小红 | 高等数学、大学物理、线性代数、概率论与数理统计 |
小李 | 高等数学、大学物理、线性代数 |
#学生表
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
#课程表
CREATE TABLE `course` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
#学生课程关系表
CREATE TABLE `student__course` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`student_id` int(11) DEFAULT NULL,
`course_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
首先我们要明确一下整个实现的步骤:
再说下这里面存在的几个坑:
举个例子,我们最开始的想法应该是这样的:
SELECT
s0.`name` AS sName,
c0.`name` AS cName
FROM
student s0
LEFT JOIN student__course sc0 ON s0.id = sc0.student_id
LEFT JOIN course c0 ON sc0.course_id = c0.id
WHERE
c0.`name` = '高等数学'
LIMIT 0,5;
查询结果显然是错误的:
那就会想到用子查询+EXISTS或者IN的方法:
-- EXISTS
SELECT
s0.`name` AS sName,
c0.`name` AS cName
FROM
student s0
LEFT JOIN student__course sc0 ON s0.id = sc0.student_id
LEFT JOIN course c0 ON sc0.course_id = c0.id
WHERE
EXISTS (
SELECT
s1.id
FROM
student s1
LEFT JOIN student__course sc1 ON s1.id = sc1.student_id
LEFT JOIN course c1 ON sc1.course_id = c1.id
WHERE
c1.`name` = '高等数学'
AND s0.id = s1.id
);
-- IN
SELECT
s0.`name` AS sName,
c0.`name` AS cName
FROM
student s0
LEFT JOIN student__course sc0 ON s0.id = sc0.student_id
LEFT JOIN course c0 ON sc0.course_id = c0.id
WHERE
s0.id IN (
SELECT
s1.id
FROM
student s1
LEFT JOIN student__course sc1 ON s1.id = sc1.student_id
LEFT JOIN course c1 ON sc1.course_id = c1.id
WHERE
c1.`name` = '高等数学'
GROUP BY
s1.id
LIMIT 0,5
);
EXISTS方法直接被排出,因为无法实现分页,limit加在exists语句中毫无意义。
IN方法看似可行,但在MYSQL某些版本下无法在IN子查询中LIMIT。
-- 查询分页数据
SELECT
s.`name` AS sName,
c.`name` AS cName
FROM
student s
LEFT JOIN student__course sc ON s.id = sc.student_id
LEFT JOIN course c ON sc.course_id = c.id
INNER JOIN (
SELECT
s.id
FROM
student s
LEFT JOIN student__course sc ON s.id = sc.student_id
LEFT JOIN course c ON sc.course_id = c.id
GROUP BY
s.id
HAVING
FIND_IN_SET('高等数学',GROUP_CONCAT( c.`name` ))
LIMIT 0,5
) AS ids ON s.id = ids.id;
-- 查询数据总条数
SELECT
COUNT( DISTINCT temp.sId )
FROM
(
SELECT
s.id AS sId,
s.`name` AS sName,
c.`name` AS cName
FROM
student s
LEFT JOIN student__course sc ON s.id = sc.student_id
LEFT JOIN course c ON sc.course_id = c.id
INNER JOIN (
SELECT
s.id
FROM
student s
LEFT JOIN student__course sc ON s.id = sc.student_id
LEFT JOIN course c ON sc.course_id = c.id
GROUP BY
s.id
HAVING
FIND_IN_SET('高等数学',GROUP_CONCAT( c.`name` ))
) AS ids ON s.id = ids.id
) AS temp;
该操作分两步:
在这里已经返回了正确的数据,最后一步就是在xml文件中写对应的resultMap即可。
在xml中多个HAVING条件使用:
<trim prefix="HAVING" suffixOverrides="AND">
<if test="? != null">
FIND_IN_SET( #{?}, GROUP_CONCAT(xx.id )) AND
</if>
<if test="? != null">
FIND_IN_SET( #{?}, GROUP_CONCAT( xx.id ))
</if>
</trim>
关于效率问题,在万条数据测试下,存在查询缓慢问题,优化方法就是加中间表字段的索引,性能会有很大提升。
到此文章结束,如果您有更好的方法欢迎评论或者私信我,大家共同分享技术共同提升,感谢阅读。