Exists 和not in 的使用

本文深入探讨了SQL查询中的高级技巧,特别是如何利用LEFT JOIN和GROUP BY结合HAVING子句来筛选平均成绩低于60分的学生信息。通过具体实例,展示了如何处理有成绩和无成绩的学生记录,同时对比了EXISTS和NOT EXISTS在查询中的应用,帮助读者掌握复杂SQL查询的构造方法。

 

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (
  `s_id` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
  `s_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
  `s_birth` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
  `s_sex` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
  PRIMARY KEY (`s_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of student
-- ----------------------------
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', '女');

SET FOREIGN_KEY_CHECKS = 1;

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for score
-- ----------------------------
DROP TABLE IF EXISTS `score`;
CREATE TABLE `score`  (
  `s_id` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
  `c_id` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
  `s_score` int(3) NULL DEFAULT NULL,
  PRIMARY KEY (`s_id`, `c_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of score
-- ----------------------------
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);

SET FOREIGN_KEY_CHECKS = 1;

-- 4、查询平均成绩小于60分的同学的学生编号和学生姓名和平均成绩**
 ​        **-- (包括有成绩的和无成绩的)**
 select st.s_id ,st.s_name ,Round(avg(s.s_score),2) as score from student st 
    left join score s on s.s_id = st.s_id group by s.s_id having Round(avg(s.s_score),2) < 60
  union 
  SELECT st.s_id ,st.s_name ,0 as score from student st where s_id not in ( select s_id from score )

 select st.s_id ,st.s_name ,Round(avg(s.s_score),2) as score from student st 
  left join score s on s.s_id = st.s_id group by s.s_id having Round(avg(s.s_score),2) < 60
  union 
  SELECT st.s_id ,st.s_name ,0 as score from student st where  not exists ( select s.s_id from score s where s.s_id = st.s_id  )
 
 

exists 使用的主要注意: -- 如果exists后面的语句查询出来有值则为真,会将SELECT * from student st  查询结果全部展示
 如果exists后面语句有 student 表中的条件,则会使用过滤条件。


 -- //**** not exists后面一般要加 查询表如student 中的条件,才能筛选出来,如果没有student表中的条件,那么结果会如下语句查询不出来
 -- //没有SELECT * from student st 结果 一个符合 not  exists 后面 (SELECT s_id from score s)的值  因此查不出来值
 SELECT * from student st where  not  exists (SELECT s_id from score s)
    -- // SELECT * from student st 结果符合 not  exists 后面 (SELECT s_id from score s  where s.s_id = st.s_id )的值, 因此有值
 SELECT * from student st where    exists (SELECT s_id from score s where s.s_id = st.s_id)

 -- 如果exists后面的语句查询出来有值则为真,会将SELECT * from student st  查询结果全部展示
 SELECT * from student st where   exists (SELECT 1 from score s where s.s_id =2 )
 -- 如果exists后面语句有 student 表中的条件,则会使用过滤条件。
 SELECT * from student st where   exists (SELECT 1 from score s where s.s_id =2 and st.s_id = 3)

 SELECT * from student st where  not  exists (SELECT 1 from score s where s.s_id =2 and st.s_id = 3)

 SELECT * from student st where not exists (SELECT 1 from score s where s.s_id  =st.s_id  AND st.s_name = '赵雷')
 

在SQL中,`NOT EXISTS``NOT IN`是用于处理子查询的两种不同操作符,它们的主要差异在于逻辑处理方式、性能表现以及适用场景。 ### 逻辑差异 `NOT IN`用于查找不在子查询结果集中的值,其逻辑是两值逻辑(True/False)。如果子查询中包含`NULL`值,则整个`NOT IN`条件会返回`False`,因为`NULL`表示未知值,无法确定某个值是否不在未知集合中[^4]。 `NOT EXISTS`则基于三值逻辑(True/False/Unknown),它检查子查询是否没有返回任何行。即使子查询中包含`NULL`值,`NOT EXISTS`也能正确处理,因为它只关心是否存在匹配的行,而不关心具体的值[^1]。 ### 性能差异 在性能方面,`NOT EXISTS`通常优于`NOT IN`,尤其是在子查询可能返回`NULL`值的情况下。数据库优化器对`NOT EXISTS`的处理更为高效,它可以利用索引半连接优化策略来加速查询。而`NOT IN`在处理包含`NULL`的子查询时,可能会导致全表扫描,从而影响性能[^2]。 此外,`NOT EXISTS`适用于外部查询内部查询都较大时的场景,而`NOT IN`则在子查询较小的情况下表现较好。如果两个表的大小相当,`NOT EXISTS``NOT IN`的性能差异不大[^4]。 ### 使用场景 - **`NOT IN`** 更适合子查询结果集明确且不包含`NULL`值的情况。例如,当你确信子查询返回的列表是完整的,并且不需要处理`NULL`值时,可以使用`NOT IN`。 示例: ```sql SELECT * FROM employees WHERE department_id NOT IN (SELECT department_id FROM departments WHERE location_id = 100); ``` - **`NOT EXISTS`** 更适合子查询可能包含`NULL`值或需要处理复杂逻辑的场景。由于`NOT EXISTS`能够正确处理`NULL`值,因此在实际应用中更为安全灵活。 示例: ```sql SELECT * FROM employees e WHERE NOT EXISTS ( SELECT 1 FROM departments d WHERE d.department_id = e.department_id AND d.location_id = 100 ); ``` ### 总结 尽管`NOT IN``NOT EXISTS`都可以用于查找不在子查询结果集中的记录,但`NOT EXISTS`在处理`NULL`值性能优化方面更具优势。因此,在大多数情况下推荐使用`NOT EXISTS`,尤其是在子查询可能返回`NULL`值或数据量较大的场景中[^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值