说明: 本文是本人的练习sql的学习笔记,sql自己写的,并且通过对比网上别人的sql来学习多种方式实现,锻炼自己多种解法的能力,并学习别人的语句的优美的地方。题目等都来自这里,https://blog.csdn.net/fashion2014/article/details/78826299
union的用法,也就是UNION 内部的每个 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每个 SELECT 语句中的列的顺序必须相同。
函数:group_concat(),手册上说明:该函数返回带有来自一个组的连接的非NULL值的字符串结果。
CASE WHEN condition THEN result
WHEN condition THEN result
.............
[WHEN ...]
[ELSE result]
END
CASE 子句可以用于任何表达式可以有效存在的地方。 condition 是一个返回boolean 的表达式。 如果结果为真,那么 CASE 表达式的结果就是符合条件的 result。 如果结果为假,那么以相同方式搜寻任何随后的 WHEN 子句。 如果没有 WHEN condition 为真,那么 case 表达式的结果就是在 ELSE 子句里的值。 如果省略了 ELSE 子句而且没有匹配的条件, 结果为 NULL。
或其语法为:
简单Case函数
CASE sex
WHEN ‘1’ THEN ‘男’
WHEN ‘2’ THEN ‘女’
ELSE ‘其他’ END
对用户变量赋值有两种方式,一种是直接用"=“号,另一种是用”:="号。其区别在于使用set命令对用户变量进行赋值时,两种方式都可以使用;当使用select语句对用户变量进行赋值时,只能使用":=“方式,因为在select语句中,”="号被看作是比较操作符
(@i:=@i+1) 也可以写成 @i:=@i+1 ,加括号是为了视觉上看这结构更清楚些。在定义好一个变量后每次查询都会给这个变量自增,而我们每次执行查询语句获取结果后就不需要这个变量自增了,所以要把它重置为0,在表名后用逗号分格下使用 (SELECT @i:=0) as i 就可以了,说下这个as i为什么要这样用,是因为派生表必须需要一个别名,这个就是做它的别名,可以任意字符。
建表语句:
课程表
CREATE TABLE `course` (
`c_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`c_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`t_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
PRIMARY KEY (`c_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
老师表
CREATE TABLE `teacher` (
`t_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`t_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
PRIMARY KEY (`t_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
学生表
CREATE TABLE `student` (
`s_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`s_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`s_birth` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`s_sex` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
PRIMARY KEY (`s_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
成绩表
CREATE TABLE `score` (
`s_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`c_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`s_score` int(3) DEFAULT NULL,
PRIMARY KEY (`s_id`, `c_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
测试数据
INSERT INTO `course` VALUES ('01', '语文', '02');
INSERT INTO `course` VALUES ('02', '数学', '01');
INSERT INTO `course` VALUES ('03', '英语', '03');
INSERT INTO `teacher` VALUES ('01', '张三');
INSERT INTO `teacher` VALUES ('02', '李四');
INSERT INTO `teacher` VALUES ('03', '王五');
INSERT INTO `student` VALUES ('01', '赵雷', '1990-01-01', '男');
INSERT INTO `student` VALUES ('02', '钱电', '1990-12-21', '男');
INSERT INTO `student` VALUES ('03', '孙风', '1990-05-20', '男');
INSERT INTO `student` VALUES ('04', '李云', '1990-08-06', '男');
INSERT INTO `student` VALUES ('05', '周梅', '1991-12-01', '女');
INSERT INTO `student` VALUES ('06', '吴兰', '1992-03-01', '女');
INSERT INTO `student` VALUES ('07', '郑竹', '1989-07-01', '女');
INSERT INTO `student` VALUES ('08', '王菊', '1990-01-20', '女');
INSERT INTO `score` VALUES ('01', '01', 80);
INSERT INTO `score` VALUES ('01', '02', 90);
INSERT INTO `score` VALUES ('01', '03', 99);
INSERT INTO `score` VALUES ('02', '01', 70);
INSERT INTO `score` VALUES ('02', '02', 60);
INSERT INTO `score` VALUES ('02', '03', 80);
INSERT INTO `score` VALUES ('03', '01', 80);
INSERT INTO `score` VALUES ('03', '02', 80);
INSERT INTO `score` VALUES ('03', '03', 80);
INSERT INTO `score` VALUES ('04', '01', 50);
INSERT INTO `score` VALUES ('04', '02', 30);
INSERT INTO `score` VALUES ('04', '03', 20);
INSERT INTO `score` VALUES ('05', '01', 76);
INSERT INTO `score` VALUES ('05', '02', 87);
INSERT INTO `score` VALUES ('06', '01', 31);
INSERT INTO `score` VALUES ('06', '03', 34);
INSERT INTO `score` VALUES ('07', '02', 89);
INSERT INTO score
VALUES (‘07’, ‘03’, 98);
course
student
成绩表score
老师表 teacher
建议拿张纸把每张表的关系记录下,这样方便写sql的时候思考,如下图
– 思路: 这里要理解01课程和02课程都是同一样表,也就是说如果要比较两者的话,得建两个一样的表才能比较
-- 法一
SELECT
s.*,
s1.s_score AS "01_score",
s2.s_score AS "02_score"
FROM
student s
inner JOIN score s1 ON s1.s_id = s.s_id
AND s1.c_id = "01"
LEFT JOIN score s2 ON s2.s_id = s.s_id
AND s2.c_id = "02"
WHERE
s1.s_score > s2.s_score
-- 法二
SELECT
s.*,
s1.s_score AS "01_score",
s2.s_score AS "02_score"
FROM
student s,
score s1,
score s2
WHERE
s.s_id = s1.s_id
AND s.s_id = s2.s_id
AND s1.c_id = "01"
AND s2.c_id = "02"
AND s1.s_score > s2.s_score
这里第一个用inner join 也就是匹配只有学生信息有的数据才显示,第二个则必须用left join,即没匹配的也要显示出来;
SELECT
s.*,
s1.s_score AS "01_score",
s2.s_score AS "02_score"
FROM
student s
inner JOIN score s1 ON s1.s_id = s.s_id
AND s1.c_id = "01"
LEFT JOIN score s2 ON s2.s_id = s.s_id
AND s2.c_id = "02"
WHERE
s1.s_score < s2.s_score
注意,使用聚合函数的时候,必须要有group by 分组,执行顺序是先分组再过滤,
– having的执行顺序在group by之后,而where执行顺序在group by 之前,而我们是要分组要统计出来的平均值进行比较大于60才显示
select s.s_id,s.s_name, ROUND(AVG(s1.s_score),2) as s_avg
from student s
inner join score s1 on s1.s_id = s.s_id
group by s.s_id
having
s_avg >= 60
法一:
注意: 这里用到了union的用法,也就是UNION 内部的每个 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每个 SELECT 语句中的列的顺序必须相同。
select s.s_id,s.s_name, ROUND(AVG(s1.s_score),2) as s_avg
from student s
inner join score s1 on s1.s_id = s.s_id
group by s.s_id
having
s_avg < 60
union
(select s.s_id,s.s_name, '0' as s_avg
from student s
where s.s_id not in(select s_id from score ))
法二(相对于法一来说更加简洁):
SELECT
s.s_id,
s.s_name,
ROUND( IFNULL( AVG( sc.s_score ), 0 ), 2 ) AS s_avg
FROM
student s
LEFT JOIN score sc ON sc.s_id = s.s_id
GROUP BY
s.s_id
HAVING
s_avg < 60
5、查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩
注意这里不能用inner join 去连接哦,因为是所有学生,08号学生是没有课程,所以用innerjoin的话,只有匹配到的数据才显示
SELECT
s.s_id,
s.s_name,
count( s1.c_id ) AS sum_course,
SUM( s1.s_score ) AS sum_score
FROM
student s
LEFT JOIN score s1 ON s1.s_id = s.s_id
GROUP BY
s.s_id
思路,也就是统计出匹配后出来的t_id
SELECT
count( t_id )
FROM
teacher t
WHERE
t.t_name LIKE "张%"
解法一: 以老师授课为主表进行关联
SELECT
s.*
FROM
teacher t
INNER JOIN course c1 ON c1.t_id = t.t_id
INNER JOIN score s1 ON s1.c_id = c1.c_id
INNER JOIN student s ON s.s_id = s1.s_id
WHERE
t.t_name = "张三"
解法二: 以学生为主表进行关联
SELECT
a.*
FROM
student a
JOIN score b ON a.s_id = b.s_id
WHERE
b.c_id IN ( SELECT c_id FROM course WHERE t_id = ( SELECT t_id FROM teacher WHERE t_name = '张三' ) );
思路: 只要我们找出张三老师教过那些学生,然后在在学生表中排除这些学生,剩下的就是没学过张三老师的学生;----》 变成我们要找出张三老师教过哪些学生的问题,也就是我们可以课程表和老师表关联,找出张三老师教学的课程 c_id------》在成绩表中选出等于该课程的学生id
-------》 学生表中排除选出的这些学生id
SELECT
s.*
FROM
student s
WHERE
s.s_id NOT IN (
SELECT
s1.s_id
FROM
score s1
WHERE
c_id = ( SELECT c_id FROM course c1 JOIN teacher t ON t.t_id = c1.t_id WHERE t.t_name = "张三" )
)
思路: 这里课程有两个条件,并且都是一个字段的问题,那么就要有两个相同的表,我们才可以进行比较;
解法1:
SELECT
st.*
FROM
student st
INNER JOIN score sc1 ON sc1.s_id = st.s_id
LEFT JOIN score sc2 ON sc2.s_id = st.s_id
WHERE
sc1.c_id = "01"
AND sc2.c_id = "02"
解法2
select a.* from
student a,score b,score c
where a.s_id = b.s_id and a.s_id = c.s_id and b.c_id='01' and c.c_id='02';
思路: 好像如果是都有的话,我们可以用表连接,如果不是都有的话,那么不能用表连接,而是通过条件判断来获取的
SELECT DISTINCT
st.*
FROM
student st
WHERE
st.s_id IN ( SELECT sc1.s_id FROM score sc1 WHERE sc1.c_id = "01" )
AND st.s_id NOT IN ( SELECT sc.s_id FROM score sc WHERE sc.c_id IN ( "02" ) )