1. SELECT查询基础入门
SQL查询就像在图书馆找书一样,SELECT语句就是我们的"图书检索系统"。想象一下,当你走进图书馆,首先要确定想找什么书(选择列),在哪个区域找(从哪张表),以及有什么具体要求(过滤条件)。这就是SELECT查询的基本逻辑。
SELECT语句的标准格式如下:
SELECT [ALL|DISTINCT] <目标列表达式> [别名] FROM <表名或视图名> [别名] [WHERE <条件表达式>] [GROUP BY <列名1> [HAVING <条件表达式>]] [ORDER BY <列名2> [ASC|DESC]]举个最简单的例子,假设我们有一个学生表Student,包含学号(Sno)、姓名(Sname)、年龄(Sage)等字段:
-- 查询所有学生的姓名和年龄 SELECT Sname, Sage FROM Student; -- 查询计算机系的所有学生信息 SELECT * FROM Student WHERE Sdept = 'CS';在实际项目中,我经常看到新手容易犯的几个错误:
- 忘记指定WHERE条件导致全表扫描
- SELECT * 查询不必要的数据列
- 忽略DISTINCT导致结果重复
2. 单表查询的进阶技巧
2.1 列选择与计算字段
SELECT不仅可以查询原始列,还能进行各种计算和转换:
-- 计算学生出生年份(假设当前是2023年) SELECT Sname, 2023-Sage AS BirthYear FROM Student; -- 使用函数处理数据 SELECT Sname, UPPER(Sdept) AS Department FROM Student; -- 使用CASE WHEN条件表达式 SELECT Sname, CASE WHEN Sage < 20 THEN '新生' ELSE '老生' END AS StudentType FROM Student;2.2 条件过滤的艺术
WHERE子句是SQL的过滤系统,常用的条件运算符包括:
- 比较运算符:=, >, <, >=, <=, <>
- 范围检查:BETWEEN AND
- 集合检查:IN
- 模糊匹配:LIKE
- 空值判断:IS NULL
-- 查询年龄在20-23岁之间的学生 SELECT * FROM Student WHERE Sage BETWEEN 20 AND 23; -- 查询姓"张"且名字为两个字的学生 SELECT * FROM Student WHERE Sname LIKE '张_'; -- 查询没有成绩记录的选课信息 SELECT * FROM SC WHERE Grade IS NULL;2.3 排序与聚合函数
ORDER BY和聚合函数是数据分析的利器:
-- 按成绩降序排列 SELECT Sno, Grade FROM SC WHERE Cno='3' ORDER BY Grade DESC; -- 常用聚合函数 SELECT COUNT(*) AS TotalStudents, AVG(Sage) AS AvgAge, MAX(Sage) AS MaxAge FROM Student;GROUP BY的使用需要注意:
- SELECT中的非聚合列必须出现在GROUP BY中
- WHERE在分组前过滤,HAVING在分组后过滤
-- 统计各系学生人数 SELECT Sdept, COUNT(*) AS StudentCount FROM Student GROUP BY Sdept; -- 查询选课超过3门的学生 SELECT Sno, COUNT(*) AS CourseCount FROM SC GROUP BY Sno HAVING COUNT(*) > 3;3. 多表连接查询实战
3.1 连接原理与类型
连接查询就像拼图游戏,我们需要找到表之间的关联字段。常见的连接类型包括:
- 内连接(INNER JOIN):只返回匹配的行
- 左外连接(LEFT JOIN):返回左表所有行,右表不匹配则为NULL
- 右外连接(RIGHT JOIN):返回右表所有行,左表不匹配则为NULL
- 全外连接(FULL JOIN):返回左右表所有行
-- 等值连接:查询学生及其选课信息 SELECT Student.Sno, Sname, Cno, Grade FROM Student, SC WHERE Student.Sno = SC.Sno; -- 使用JOIN语法更清晰 SELECT Student.Sno, Sname, Cno, Grade FROM Student INNER JOIN SC ON Student.Sno = SC.Sno;3.2 连接性能优化
多表连接时,数据库通常采用以下算法:
- 嵌套循环连接(Nested Loop):适合小表驱动大表
- 哈希连接(Hash Join):适合中等规模数据
- 排序合并连接(Sort-Merge):适合已排序数据
-- 复合条件连接:查询选修2号课程且成绩>88的学生 SELECT Student.Sno, Sname FROM Student JOIN SC ON Student.Sno = SC.Sno WHERE SC.Cno='2' AND SC.Grade > 88;在实际项目中,连接查询最容易出现的性能问题就是笛卡尔积(忘记写连接条件),我曾见过一个查询因为漏写连接条件,导致两个百万级表的笛卡尔积,直接把数据库卡死。
4. 子查询与嵌套查询
4.1 子查询类型
子查询就像SQL中的"俄罗斯套娃",主要分为:
- 不相关子查询:子查询可独立执行
- 相关子查询:子查询依赖外部查询
-- 不相关子查询:查询与"刘晨"同系的学生 SELECT Sno, Sname, Sdept FROM Student WHERE Sdept IN (SELECT Sdept FROM Student WHERE Sname='刘晨'); -- 相关子查询:查询超过自己平均成绩的课程 SELECT Sno, Cno FROM SC x WHERE Grade >= (SELECT AVG(Grade) FROM SC y WHERE y.Sno=x.Sno);4.2 EXISTS与NOT EXISTS
EXISTS用于检查子查询是否返回结果,不关心具体数据:
-- 查询选修了1号课程的学生 SELECT Sname FROM Student WHERE EXISTS (SELECT * FROM SC WHERE Sno=Student.Sno AND Cno='1');4.3 集合查询
集合操作包括UNION(并)、INTERSECT(交)、EXCEPT(差):
-- 查询计算机系或年龄<19的学生 SELECT * FROM Student WHERE Sdept='CS' UNION SELECT * FROM Student WHERE Sage<19;5. 实战案例与性能优化
5.1 典型查询模式
-- 查询每个学生的总学分 SELECT Student.Sno, Sname, SUM(Ccredit) AS TotalCredit FROM Student JOIN SC ON Student.Sno = SC.Sno JOIN Course ON SC.Cno = Course.Cno WHERE Grade >= 60 -- 及格才算学分 GROUP BY Student.Sno, Sname; -- 查询各科成绩前三名 SELECT Cno, Sno, Grade FROM SC x WHERE (SELECT COUNT(*) FROM SC y WHERE y.Cno=x.Cno AND y.Grade>x.Grade) < 3 ORDER BY Cno, Grade DESC;5.2 性能优化建议
- 为常用连接字段创建索引
- 避免在WHERE条件中对字段进行函数操作
- 只查询需要的列,避免SELECT *
- 大数据量表连接时,先过滤再连接
- 复杂查询考虑使用临时表或视图
-- 优化示例:先过滤再连接 SELECT S.Sno, Sname, Cname, Grade FROM (SELECT * FROM Student WHERE Sdept='CS') S JOIN SC ON S.Sno = SC.Sno JOIN Course ON SC.Cno = Course.Cno;在实际项目中,我曾优化过一个需要20秒执行的查询,通过添加适当的索引和重写查询逻辑,最终将执行时间降到了0.5秒。关键是要理解数据库的执行计划,知道它如何访问和处理数据。