SQL语法基础教程

本文档详细讲解SQL的基础语法和概念。


一、SQL语句分类

1. DDL - 数据定义语言(Data Definition Language)

作用:定义和管理数据库对象(表、视图、索引等)

命令 说明 示例
CREATE 创建 CREATE TABLE 表名 (...)
ALTER 修改 ALTER TABLE 表名 ADD (列名 类型)
DROP 删除 DROP TABLE 表名
TRUNCATE 清空 TRUNCATE TABLE 表名

特点

  • DDL操作会自动提交(COMMIT)

  • 不能回滚(ROLLBACK)

  • 修改表结构时要特别小心


2. DML - 数据操纵语言(Data Manipulation Language)

作用:操作表中的数据

命令 说明 示例
SELECT 查询 SELECT * FROM 表名
INSERT 插入 INSERT INTO 表名 VALUES (...)
UPDATE 更新 UPDATE 表名 SET 列=值 WHERE 条件
DELETE 删除 DELETE FROM 表名 WHERE 条件

特点

  • DML操作需要手动COMMIT才能生效

  • 可以ROLLBACK撤销

  • 本实验主要是DML中的SELECT查询


3. DCL - 数据控制语言(Data Control Language)

作用:控制用户权限

命令 说明 示例
GRANT 授权 GRANT SELECT ON 表名 TO 用户
REVOKE 撤销 REVOKE SELECT ON 表名 FROM 用户

二、SELECT语句完整语法

基本结构

sql 复制代码
SELECT [DISTINCT] 列名
FROM 表名
[WHERE 条件]
[GROUP BY 列名]
[HAVING 条件]
[ORDER BY 列名 [ASC|DESC]]

执行顺序(重要!)

sql 复制代码
1. FROM      --> 确定查询哪个表
2. WHERE     --> 过滤行(分组前)
3. GROUP BY  --> 分组
4. HAVING    --> 过滤组(分组后)
5. SELECT    --> 选择显示的列
6. ORDER BY  --> 排序
7. DISTINCT  --> 去重

为什么顺序重要?

  • WHERE在GROUP BY之前,所以不能使用聚合函数

  • HAVING在GROUP BY之后,可以使用聚合函数

  • SELECT的别名在ORDER BY中可用,但在WHERE中不可用

示例理解

sql 复制代码
SELECT 出版单位, AVG(单价) AS 平均单价
FROM 书目
WHERE 单价 > 20              -- ✅ 可以:WHERE在SELECT前执行
-- WHERE 平均单价 > 30      -- ❌ 错误:平均单价别名还不存在
GROUP BY 出版单位
HAVING AVG(单价) > 30        -- ✅ 可以:HAVING在GROUP BY后
ORDER BY 平均单价 DESC;      -- ✅ 可以:ORDER BY在SELECT后,别名已存在

三、基础语法详解

1. SELECT - 选择列

sql 复制代码
-- 选择所有列
SELECT * FROM 书目;
​
-- 选择指定列
SELECT 书名, 作者 FROM 书目;
​
-- 使用别名
SELECT 书名 AS 图书名称, 作者 AS 作者姓名 FROM 书目;
-- AS可以省略
SELECT 书名 图书名称, 作者 作者姓名 FROM 书目;
​
-- 计算列
SELECT 书名, 单价, 单价 * 0.8 AS 折扣价 FROM 书目;

DISTINCT去重

sql 复制代码
-- 查询所有不同的出版单位
SELECT DISTINCT 出版单位 FROM 书目;
​
-- 查询所有不同的(出版单位, 图书分类号)组合
SELECT DISTINCT 出版单位, 图书分类号 FROM 书目;

2. FROM - 指定表

sql 复制代码
-- 单表
FROM 书目
​
-- 表别名
FROM 书目 s            -- s是别名
FROM 书目 AS s         -- AS可加可不加

为什么要用表别名?

  1. 简化书写:s.书名书目.书名 简洁

  2. 多表查询时必需:区分同名列

  3. 自连接时必需:同一个表要用不同别名


3. WHERE - 过滤行

基本比较运算符

运算符 说明 示例
= 等于 WHERE 单价 = 30
!= 或 <> 不等于 WHERE 单价 <> 30
> 大于 WHERE 单价 > 30
>= 大于等于 WHERE 单价 >= 30
< 小于 WHERE 单价 < 30
<= 小于等于 WHERE 单价 <= 30

逻辑运算符

运算符 说明 示例
AND 且(都要满足) WHERE 单价 > 20 AND 单价 < 40
OR 或(满足一个即可) WHERE 出版单位='人民出版社' OR 出版单位='作家出版社'
NOT 非(取反) WHERE NOT 出版单位='人民出版社'

范围查询

sql 复制代码
-- BETWEEN...AND... (包含边界值)
WHERE 单价 BETWEEN 20 AND 40
-- 等价于
WHERE 单价 >= 20 AND 单价 <= 40
​
-- IN (...) 在列表中
WHERE 出版单位 IN ('人民出版社', '作家出版社')
-- 等价于
WHERE 出版单位='人民出版社' OR 出版单位='作家出版社'

模糊查询

sql 复制代码
-- LIKE 模糊匹配
-- % : 匹配任意长度的任意字符(包括0个)
-- _ : 匹配单个任意字符
​
WHERE 书名 LIKE '红%'      -- 以"红"开头
WHERE 书名 LIKE '%梦'      -- 以"梦"结尾
WHERE 书名 LIKE '%数据库%'  -- 包含"数据库"
WHERE 书名 LIKE '红_梦'    -- "红"和"梦"之间有一个字符
WHERE 书名 LIKE '__'       -- 恰好两个字符的书名

NULL值判断

sql 复制代码
-- 判断是否为NULL
WHERE 归还日期 IS NULL         -- ✅ 正确
WHERE 归还日期 = NULL          -- ❌ 错误(永远为FALSE)

-- 判断是否不为NULL
WHERE 归还日期 IS NOT NULL     -- ✅ 正确
WHERE 归还日期 != NULL         -- ❌ 错误(永远为FALSE)

为什么不能用 = NULL?

  • NULL表示"未知"

  • "未知"不等于任何值,包括"未知"本身

  • NULL = NULL 的结果是NULL(不是TRUE也不是FALSE)

  • 只有IS NULL能正确判断NULL


4. ORDER BY - 排序

sql 复制代码
-- 单列排序
ORDER BY 单价 ASC         -- 升序(从小到大),ASC可省略
ORDER BY 单价 DESC        -- 降序(从大到小)
ORDER BY 单价             -- 默认升序

-- 多列排序
ORDER BY 单价 DESC, 书名 ASC
-- 先按单价降序,单价相同再按书名升序

-- 使用列的位置(不推荐,可读性差)
SELECT 书名, 单价 FROM 书目
ORDER BY 2 DESC;          -- 按第2列(单价)降序

-- 使用别名
SELECT 书名, 单价 AS 价格 FROM 书目
ORDER BY 价格 DESC;       -- 使用别名排序

NULL值的排序

sql 复制代码
ORDER BY 归还日期         -- NULL值排在最前面
ORDER BY 归还日期 NULLS LAST  -- NULL值排在最后面

5. 聚合函数

五大聚合函数

sql 复制代码
-- COUNT(*) : 统计行数(包括NULL)
SELECT COUNT(*) FROM 借阅;                -- 总借阅次数

-- COUNT(列) : 统计非NULL值的数量
SELECT COUNT(归还日期) FROM 借阅;         -- 已归还的次数

-- SUM(列) : 求和
SELECT SUM(单价) FROM 书目;               -- 所有书的单价总和

-- AVG(列) : 平均值
SELECT AVG(单价) FROM 书目;               -- 平均单价

-- MAX(列) : 最大值
SELECT MAX(单价) FROM 书目;               -- 最贵的书价格

-- MIN(列) : 最小值
SELECT MIN(单价) FROM 书目;               -- 最便宜的书价格

COUNT(*)和COUNT(列)的区别

sql 复制代码
-- 假设表中有5行,其中2行的归还日期为NULL

SELECT COUNT(*) FROM 借阅;              -- 结果:5(包括NULL)
SELECT COUNT(归还日期) FROM 借阅;       -- 结果:3(只统计非NULL)
SELECT COUNT(DISTINCT 借书证号) FROM 借阅;  -- 统计不同的借书证号数量

注意事项

  1. 聚合函数忽略NULL值(COUNT(*)除外)

  2. 聚合函数不能用在WHERE中(要用HAVING)

  3. SELECT中有聚合函数时,其他列要么也是聚合函数,要么在GROUP BY中


6. GROUP BY - 分组

基本语法

sql 复制代码
SELECT 分组列, 聚合函数(列)
FROM 表名
GROUP BY 分组列;

示例

sql 复制代码
-- 统计每个出版社的书目数量
SELECT 出版单位, COUNT(*) AS 书目数量
FROM 书目
GROUP BY 出版单位;

-- 统计每个出版社的书目数量和平均单价
SELECT 
    出版单位, 
    COUNT(*) AS 书目数量,
    AVG(单价) AS 平均单价
FROM 书目
GROUP BY 出版单位;

GROUP BY的规则

规则1:SELECT中非聚合列必须在GROUP BY中

sql 复制代码
-- ❌ 错误:书名不在GROUP BY中
SELECT 出版单位, 书名, COUNT(*) 
FROM 书目
GROUP BY 出版单位;

-- ✅ 正确:书名加入GROUP BY
SELECT 出版单位, 书名, COUNT(*) 
FROM 书目
GROUP BY 出版单位, 书名;

为什么?

  • GROUP BY将数据分组,每组返回一行

  • 如果书名不在GROUP BY中,数据库不知道该显示该组的哪个书名

规则2:GROUP BY中的列可以不在SELECT中

sql 复制代码
-- ✅ 正确:按出版单位分组,但不显示出版单位
SELECT COUNT(*) AS 总数量
FROM 书目
GROUP BY 出版单位;

7. HAVING - 过滤分组

HAVING vs WHERE

特性 WHERE HAVING
作用对象 行(原始数据) 组(分组后的结果)
执行时机 GROUP BY之前 GROUP BY之后
能否使用聚合函数

示例对比

sql 复制代码
-- 查询单价>20的书,按出版社分组,显示平均单价>30的出版社

SELECT 出版单位, AVG(单价) AS 平均单价
FROM 书目
WHERE 单价 > 20              -- WHERE: 过滤行(分组前)
GROUP BY 出版单位
HAVING AVG(单价) > 30        -- HAVING: 过滤组(分组后)
ORDER BY 平均单价 DESC;

-- 执行流程:
-- 1. FROM 书目
-- 2. WHERE 单价 > 20  (先过滤掉单价<=20的书)
-- 3. GROUP BY 出版单位  (对剩余的书按出版社分组)
-- 4. HAVING AVG(单价) > 30  (过滤掉平均单价<=30的组)
-- 5. SELECT
-- 6. ORDER BY

常见错误

sql 复制代码
-- ❌ 错误:WHERE中不能用聚合函数
SELECT 出版单位, AVG(单价)
FROM 书目
WHERE AVG(单价) > 30        -- 错误!WHERE不能用AVG
GROUP BY 出版单位;

-- ✅ 正确:应该用HAVING
SELECT 出版单位, AVG(单价)
FROM 书目
GROUP BY 出版单位
HAVING AVG(单价) > 30;

四、JOIN连接查询

1. 内连接(INNER JOIN)

语法

sql 复制代码
SELECT 列名
FROM 表1
INNER JOIN 表2 ON 连接条件;
-- INNER可以省略,直接写JOIN

特点

  • 只返回两表都有匹配的记录

  • 相当于两表的交集

示例

sql 复制代码
-- 查询借阅信息(只显示有借阅记录的读者)
SELECT d.姓名, s.书名, j.借书日期
FROM 借阅 j
INNER JOIN 读者 d ON j.借书证号 = d.借书证号
INNER JOIN 图书 t ON j.图书编号 = t.图书编号
INNER JOIN 书目 s ON t.ISBN = s.ISBN;

2. 左外连接(LEFT JOIN)

语法

sql 复制代码
SELECT 列名
FROM 表1
LEFT JOIN 表2 ON 连接条件;

特点

  • 返回左表所有记录

  • 右表没有匹配时显示NULL

示例

sql 复制代码
-- 查询所有读者的借阅次数(包括没借过书的)
SELECT 
    d.姓名,
    COUNT(j.借阅流水号) AS 借阅次数
FROM 读者 d
LEFT JOIN 借阅 j ON d.借书证号 = j.借书证号
GROUP BY d.姓名;

-- 结果会包括借阅次数为0的读者

LEFT JOIN vs INNER JOIN

sql 复制代码
-- INNER JOIN:只显示有借阅记录的读者
FROM 读者 d
INNER JOIN 借阅 j ON d.借书证号 = j.借书证号
-- 结果:只有借过书的读者

-- LEFT JOIN:显示所有读者
FROM 读者 d
LEFT JOIN 借阅 j ON d.借书证号 = j.借书证号
-- 结果:所有读者,没借过书的借阅次数显示为0或NULL

3. 右外连接(RIGHT JOIN)

语法

sql 复制代码
SELECT 列名
FROM 表1
RIGHT JOIN 表2 ON 连接条件;

特点

  • 返回右表所有记录

  • 左表没有匹配时显示NULL

  • 不常用(可以调换表顺序用LEFT JOIN替代)


4. 全外连接(FULL JOIN)

语法

sql 复制代码
SELECT 列名
FROM 表1
FULL JOIN 表2 ON 连接条件;

特点

  • 返回两表所有记录

  • 相当于LEFT JOIN + RIGHT JOIN

  • Oracle支持,MySQL不支持


五、子查询

1. 标量子查询(返回单个值)

sql 复制代码
-- 查询单价最高的图书
SELECT * FROM 书目
WHERE 单价 = (SELECT MAX(单价) FROM 书目);

-- 子查询返回一个值:最高单价

2. 列子查询(返回一列值)

sql 复制代码
-- 查询借过书的读者
SELECT * FROM 读者
WHERE 借书证号 IN (SELECT DISTINCT 借书证号 FROM 借阅);

-- 子查询返回一列值:所有借过书的借书证号

IN vs EXISTS

sql 复制代码
-- 使用IN
WHERE 借书证号 IN (SELECT 借书证号 FROM 借阅)

-- 使用EXISTS(性能通常更好)
WHERE EXISTS (SELECT 1 FROM 借阅 j WHERE j.借书证号 = d.借书证号)

3. 表子查询(返回一个表)

sql 复制代码
-- 查询借阅次数>2的读者
SELECT * FROM (
    SELECT 借书证号, COUNT(*) AS 借阅次数
    FROM 借阅
    GROUP BY 借书证号
) WHERE 借阅次数 > 2;

-- 子查询返回一个临时表

六、常用函数

1. 字符串函数

sql 复制代码
-- 连接字符串
SELECT 书名 || '(' || 作者 || ')' AS 完整信息 FROM 书目;

-- 转换大小写
SELECT UPPER(书名), LOWER(书名) FROM 书目;

-- 截取字符串
SELECT SUBSTR(书名, 1, 2) FROM 书目;  -- 截取前2个字符

-- 字符串长度
SELECT LENGTH(书名) FROM 书目;

2. 数值函数

sql 复制代码
-- 四舍五入
SELECT ROUND(单价, 1) FROM 书目;  -- 保留1位小数

-- 向上取整
SELECT CEIL(单价) FROM 书目;

-- 向下取整
SELECT FLOOR(单价) FROM 书目;

-- 截断小数
SELECT TRUNC(单价, 1) FROM 书目;  -- 保留1位小数,不四舍五入

3. 日期函数

sql 复制代码
-- 当前日期时间
SELECT SYSDATE FROM DUAL;

-- 日期相减(结果是天数)
SELECT SYSDATE - 借书日期 AS 已借天数 FROM 借阅;

-- 日期加减天数
SELECT 借书日期 + 30 AS 应还日期 FROM 借阅;

-- 加减月份
SELECT ADD_MONTHS(借书日期, 1) AS 一个月后 FROM 借阅;

-- 提取年月日
SELECT 
    EXTRACT(YEAR FROM 借书日期) AS 年份,
    EXTRACT(MONTH FROM 借书日期) AS 月份,
    EXTRACT(DAY FROM 借书日期) AS 日期
FROM 借阅;

-- 日期格式化
SELECT TO_CHAR(借书日期, 'YYYY-MM-DD') AS 格式化日期 FROM 借阅;

4. NULL处理函数

sql 复制代码
-- NVL(值1, 值2) : 如果值1为NULL,返回值2
SELECT NVL(罚金, 0) AS 罚金 FROM 罚款分类;

-- NVL2(值1, 值2, 值3) : 如果值1非NULL返回值2,否则返回值3
SELECT NVL2(归还日期, '已还', '未还') AS 状态 FROM 借阅;

-- COALESCE(值1, 值2, 值3, ...) : 返回第一个非NULL值
SELECT COALESCE(归还日期, 借书日期, SYSDATE) FROM 借阅;

5. CASE表达式

sql 复制代码
-- 简单CASE
SELECT 
    书名,
    CASE 是否借出
        WHEN '是' THEN '已借出'
        WHEN '否' THEN '可借'
        ELSE '未知'
    END AS 状态
FROM 图书;

-- 搜索CASE(更常用)
SELECT 
    书名,
    单价,
    CASE 
        WHEN 单价 < 20 THEN '低价'
        WHEN 单价 < 40 THEN '中价'
        ELSE '高价'
    END AS 价格等级
FROM 书目;

七、常见错误及解决

错误1:SELECT的列不在GROUP BY中

sql 复制代码
-- ❌ 错误
SELECT 出版单位, 书名, COUNT(*)
FROM 书目
GROUP BY 出版单位;

-- 错误信息:ORA-00979: not a GROUP BY expression

-- ✅ 解决:把书名加入GROUP BY
SELECT 出版单位, 书名, COUNT(*)
FROM 书目
GROUP BY 出版单位, 书名;

错误2:WHERE中使用聚合函数

sql 复制代码
-- ❌ 错误
SELECT 出版单位, AVG(单价)
FROM 书目
WHERE AVG(单价) > 30
GROUP BY 出版单位;

-- 错误信息:ORA-00934: group function is not allowed here

-- ✅ 解决:用HAVING代替WHERE
SELECT 出版单位, AVG(单价)
FROM 书目
GROUP BY 出版单位
HAVING AVG(单价) > 30;

错误3:NULL值判断错误

sql 复制代码
-- ❌ 错误
SELECT * FROM 借阅
WHERE 归还日期 = NULL;

-- 查不到任何结果(因为NULL=NULL的结果是NULL,不是TRUE)

-- ✅ 解决:用IS NULL
SELECT * FROM 借阅
WHERE 归还日期 IS NULL;

错误4:字符串忘记加引号

sql 复制代码
-- ❌ 错误
SELECT * FROM 书目
WHERE 书名 = 红楼梦;

-- 错误信息:ORA-00904: "红楼梦": invalid identifier

-- ✅ 解决:字符串要用单引号
SELECT * FROM 书目
WHERE 书名 = '红楼梦';

错误5:日期格式错误

sql 复制代码
-- ❌ 错误
INSERT INTO 借阅 VALUES (1, '20051001', '2001231', '2010-09-19', NULL, NULL, NULL);

-- 可能报错或插入错误的日期

-- ✅ 解决:使用TO_DATE函数
INSERT INTO 借阅 VALUES (
    1, '20051001', '2001231', 
    TO_DATE('2010-09-19', 'YYYY-MM-DD'), 
    NULL, NULL, NULL
);

八、学习建议

1. 从简单到复杂

  1. 第一步:单表查询(SELECT、WHERE、ORDER BY)

  2. 第二步:聚合统计(COUNT、SUM、AVG、GROUP BY)

  3. 第三步:多表连接(JOIN)

  4. 第四步:子查询

  5. 第五步:复杂综合查询

2. 多练习、多思考

  • 不要只看代码,要动手写

  • 理解为什么这么写

  • 尝试用不同方法解决同一个问题

3. 理解执行流程

  • 记住SELECT语句的执行顺序

  • 理解每个子句的作用

  • 知道为什么有些语法不能用

4. 善用注释

  • 在复杂查询中添加注释

  • 说明查询的目的和逻辑

  • 方便日后维护


九、快速参考

SQL关键字速查

关键字 作用 位置
SELECT 选择列 开头
FROM 指定表 SELECT后
WHERE 过滤行 FROM后
GROUP BY 分组 WHERE后
HAVING 过滤组 GROUP BY后
ORDER BY 排序 最后
JOIN 连接表 FROM中
AND/OR 逻辑运算 WHERE/HAVING中
IN 在列表中 WHERE中
LIKE 模糊查询 WHERE中
IS NULL 判断NULL WHERE中

运算符优先级

  1. 括号 ()

  2. 比较运算 =, !=, >, <, >=, <=

  3. NOT

  4. AND

  5. OR

建议:使用括号明确优先级,提高可读性


希望这份教程能帮助你更好地理解SQL语法!继续加油!💪

相关推荐
老华带你飞3 小时前
社区养老保障|智慧养老|基于springboot+小程序社区养老保障系统设计与实现(源码+数据库+文档)
java·数据库·vue.js·spring boot·小程序·毕设·社区养老保障
Coding_Doggy3 小时前
链盾shieldchiain | 团队功能、邀请成员、权限修改、移除成员、SpringSecurity、RBAC权限控制
java·开发语言·数据库
凯子坚持 c3 小时前
不用复杂配置!本地 Chat2DB 秒变远程可用,跨网操作数据库就这么简单
数据库
q***65693 小时前
Windows环境下安装Redis并设置Redis开机自启
数据库·windows·redis
q***96584 小时前
Windows版Redis本地后台启动
数据库·windows·redis
q***81644 小时前
【Redis】centos7 systemctl 启动 Redis 失败
数据库·redis·缓存
q***09804 小时前
MySQL 常用 SQL 语句大全
数据库·sql·mysql
q***64974 小时前
VS与SQL Sever(C语言操作数据库)
c语言·数据库·sql
无敌最俊朗@4 小时前
Qt面试题day01
java·数据库·面试