本文参考SQL基础教程编写,建立在曾学习过相关课程的基础上,进行回顾。如您未曾接触过SQL,阅读本文可能会有些吃力。
语法规范
- MySQL本身不区分大小写,但强烈要求关键字大写,表名、列名用小写;
- 创建表时,使用统一的、描述性强的字段命名规则保证字段名是独一无二且不是保留字的,不要使用连续的下划线,不用下划线结尾;最好以字母开头
- 关键字右对齐,且不同层级的用空格或缩进控制,使其区分开。
- 列名少的时候写在一行里无伤大雅;多的时候以及涉及到CASE WHEN 或者聚合计算的时候,建议分行写;个人习惯是逗号在列名前面,方便之后删除某些列,放列名后亦可;
- 表别名和列别名尽量用有具体含义的词组,不要用
a b c
,不然以后review的时候会非常痛苦; - 运算符前后都加一个空格;
- 当用到多个表时,请在所有列名前写上引用的表别名,不要嫌麻烦;
- 每条命令用分号结尾;
- 养成随手写注释的习惯,注释方法:
1
2
3
4
5#单行注释
-- 单行注释
/*
多行注释
*/
SQL语句分类
DDL
DDL(Data Definition Language,数据定义语言) 用来创建或者删除存储数据用的数据库以及数据库中的表等对象。(create、drop、alter)
DML
DML(Data Manipulation Language,数据操纵语言) 用来查询或者变更表中的记录。(select、insert、update、delete)
DCL
DCL(Data Control Language,数据控制语言) 用来确认或者取消对数据库中的数据进行的变更。除此之外,还可以对 RDBMS 的用户是否有权限操作数据库中的对象(数据库表等)进行设定。(commit、rollback、grant、revoke)
Win 系统默认不区分表名及字段名的大小写
Linux / Mac 默认严格区分表名及字段名的大小写
基本语句
1 | # 创建数据库 |
只有未设置
NOT NULL
且非主键的列才可以清空为NULL。
列约束
PRIMARY KEY
— 主键UNIQUE
— 值唯一(NULL值可重复)DEFAULT
— 默认值(使用时不写、写DEFAULT
均可以)NOT NULL
— 非空FOREIGN
— 外键AUTO_INCREMENT
— 自动成长(成长为列的最大值+1)
索引
索引的作用 — 提高查询速率
索引一般分为
- 聚集索引 — 在数据库中,所有行数都会按照主键索引排序。
- 非聚集索引 — 就是给普通字段加上索引。
- 联合索引 — 好几个字段组成的索引称为联合索引。
分类
- 主键索引
建立在主键上的索引。一张数据表只能有一个主键索引,索引列值不允许有空值,通常在创建表时一起创建。
- 唯一索引
建立在UNIQUE字段上的索引。一张表可以有多个唯一索引,索引列值允许为空,列值中出现多个空值不会发生重复冲突。
- 普通索引
建立在普通字段上的索引。
- 前缀索引
对字符类型字段的前几个字符或对二进制类型字段的前几个bytes建立索引。通常建立在类型为char、varchar、binary、varbinary
的列上。
可以大大减少索引占用的存储空间,也能提升索引的查询效率。
- 全文索引
全文索引技术就是将各种信息,文档中所有的文字序列都作为检索对象,找出包含检索词汇的信息或文档。
查找
找某一列
1 | SELECT [DISTINCT] <列名> FROM <表名>; |
*
查找全部- 随意使用换行符,不影响语句执行。
DISTINCT
用来去重。-- 单行注释
&/* 多行注释*/
- SELECT子句中可以使用常数或者表达式。
寻找某列满足某一条件的值所在行中的某些列
1 | SELECT <列名> |
运算符
含义 | 运算符 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
= | 相等 |
<> | 不等 |
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
NOT | 非 |
AND | 与 |
OR | 或 |
() | 改变优先级 |
NULL
既不为真也不为假。值为UNKNOWN
聚合函数
- COUNT — 行数
- SUM — 数据的和
- AVG — 数据均值
- MAX — 数据最大值
- MIN — 数据最小值
1 | SELECT FUNC(<列名>) FROM <表名> ; |
COUNT(*)
包含含有NULL
的项;COUNT(列名)
不包含NULL值。MAX/MIN
适用于所有(字典序),AVG/SUM
只适用于数值。
分组
GROUP BY
按照某几列的值进行分组(有优先级)
1 | SELECT <列名1>, <列名2>, <列名3>, |
条件
HAVING
用于解决WHERE无法与聚合函数一起使用。这一问题
1 | SELECT <列名1>, <列名2> |
排序
ORDER BY
将查询结果排序。默认为升序排列,降序排列为DESC
。
1 | SELECT <列名1>, <列名2> |
MySQL
中认为NULL
值比非NULL
小。(排序时可能需要特殊处理)- 在排序时可以使用
IS NULL
运算符及ISNULL()
函数来对NULL值做特殊处理
- 在排序时可以使用
视图
视图是一个虚拟的表,不同于直接操作数据表,视图是依据 SELECT 语句来创建的,所有操作视图时会根据创建视图的 SELECT
语句生成一张虚拟表,然后在这张虚拟表上做 SQL 操作。
视图与表的区别是「是否保存了实际的数据」
视图并不是数据库真实存储的数据表,它可以看作是一个窗口,通过这个窗口我们可以看到数据库表中真实存在的数据。
存在原因
- 可以将频繁使用的 SELECT 语句保存以提高效率。
- 可以使用户看到的数据更加清晰。
- 可以不对外公开数据表全部字段,增强数据的保密性。
- 可以降低数据的冗余。
1 | -- 创建视图 |
- SELECT 语句中列的排列顺序和视图中列的排列顺序相同
- 视图名在数据库中需要是唯一的,不能与其他视图和表重名。
更新视图
由于视图是一个虚拟表,对视图的操作就是对底层基础表的操作。
故,当视图存在以下结构时不能更新:
- 聚合函数。如,SUM()、MIN()、COUNT()
- DISTINCT 关键字
- GROUP BY 子句。
- HAVING 子句。
- UNION 或 UNION ALL 运算符。
- FROM 子句中包含多个表。
1 | UPDATE <视图名称> |
删除视图
1 | DROP VIEW <视图名1> |
子查询
一个查询语句嵌套在另一个查询语句内部。子查询结果作为外层查询的过滤条件,查询可以基于一个表或者多个表。
不建议嵌套多层子查询
虽然嵌套子查询可以查询出结果,但是随着子查询嵌套的层数的叠加,SQL语句不仅会难以理解而且执行效率也会很差,所以要尽量避免这样的使用。
标量子查询
执行的SQL语句只能返回一个值,也就是要返回表中具体的某一行的某一列。
标量子查询不仅仅局限于 WHERE 子句中,通常任何可以使用单一值的位置都可以使用。
1 | -- 查找价格高于平均值的商品 |
这里出现的
AS
用来起别名。创建别名是为了让列名称的可读性更强。
关联子查询
关联子查询,在细分的组内进行比较时使用。
需要特别注意其与一般的SQL语句相比,执行顺序有所不同。
- 首先执行不带WHERE的主查询,获取到所关联的列,依次(一次传一行)传入子查询。
- 根据主查询传入的结果匹配子查询,将查询结果返回父查询
- 根据子查询返回值依次判断父查询的
WHERE
子句。
以下列代码为例
1 | /******************** |
- 首先执行主查询
SELECT student_id,course_id,course_score FROM score AS s1
提取出student_id,course_id、course_score
三列所需要的信息 - 执行
()
中的子查询语句,依次将course_id
传入s1.course_id= s2.course_id
并执行得到- 如第一步查询出课程编号为
10001 10002 10003 10004
- 依次将课程号传入相应位置 执行
SELECT AVG(course_score) FROM score AS s2 WHERE course_id = 10001 GROUP BY course_id
根据这门课的所有人成绩计算该门课的平均分 - 依次将每门课的平均分
val
值返回给主查询。
- 如第一步查询出课程编号为
- 然后依次执行
SELECT student_id,course_id,course_score FROM score AS s1 WHERE course_score > val
。得到每门课成绩大于平均分的学生的信息。
其中 需要注意的是 看似是废话的
WHERE s1.course_id = s2.course_id
即为关联子查询的关键点。(关联条件)
函数
SQL内置了一些函数,极大提高了SQL语言的便利性。分类如下
- 算术函数
- 字符串函数
- 日期函数
- 转换函数
- 聚合函数
算术函数
- ABS(val) — 绝对值。ABS(NULL) 返回NULL
- MOD(val, pow) — 取余。
- ROUND(val, n) — 四舍五入保留n位小数。
字符串函数
- CONCAT(str1, str2) — 字符串拼接
- LENGTH(str) — 字符串长度
- LOWER(str) — 转换成小写
- UPPER(str) — 转换成大写
- REPLACE(str,ch1,ch2) — 将str中
ch1
全部替换为ch2
- SUBSTRING(str FROM pos FOR n) — 在str中pos位置开始提取长度为n的子串
- SUBSTRING_INDEX(str,ch,n) — 以ch为分隔符划分str,并获取第n个分隔符前的所有内容
- REPEAT(str,n) — 将字符串str重复n次
日期函数
- CURRENT_DATE — 获取当前日期
- CURRENT_TIME — 获取当前时间
- CURRENT_TIMESTAMP — 获取当前日期和时间
- EXTRACT(日期元素 FROM 日期) — 从日期中截取信息
- EXTRACT(YEAR FROM CURRENT_TIMESTAMP) AS year 获取年份。
- 日期元素有
YEAR MONTH DAY HOUR MINUTE SECOND
转换函数
- CAST(val,type) — 将值转换成相应的类型
- COALESCE(val1,val2,….) — 获取所有参数中第一个非NULL值。
谓词
表示个体的性质和个体之间关系的符号。主要包括LIKE、BETWEEN、IS NULL、IS NOT NULL、IN 、EXISTS
LIKE
用于字符串的部分匹配
1 | SELECT <列名1>, <列名2> |
字符串支持正则表达式。
%
表示任意个数字符。例如,%xxx%
表示含有xxx
的所有字符串。_
表示一个字符。例如_xxx%
表示第2~4位为xxx
的所有字符串。[x]
表示列表中的一个字符。例如[abcd]xxx
表示axxx
或bxxx
或cxxx
或dxxx
[^x] 、 [!x]
表示不在列表中的字符。[^abc]xxx
表示长度为4,首位不为a b c
,后三位为xxx
的字符串
BETWEEN
用于范围查询。
1 | SELECT product_name, sale_price |
BETWEEN val1 AND val2
包括左右临界值。
IS NULL/ IS NOT NULL
取空或非空(有些条件下不能使用= <>
,比如取值为空的数据)
1 | SELECT product_name, purchase_price |
IN/ NOT IN
多条件并集的查询。
1 | SELECT product_name, purchase_price |
IN 还可以用在子查询中
比如某连锁商店有很多个分店,不同分店的商品不完全一样
我们查询某个分店的是否有某个商品时
子查询返回该分店的所有商品
主查询判断是否在这个范围内
EXIST/NOT EXIST
「判断是否存在满足某种条件的记录」。存在,返回true
;不存在,返回false
。
1 | -- 选出某个分店在售商品(价格不为NULL)的单价 |
由于 EXIST 只关心记录是否存在,返回哪些列都没有关系。
CASE
条件分支。
1 | CASE WHEN <求值表达式> THEN <表达式> |
集合的运算
表、视图、查询结果
都是记录的集合, 其中的元素为「表或者查询结果」中的每一行。
UNION、INTERSECT、EXCEPT
来将检索结果进行集合运算。
并集
UNION,重复项会去重。
1 | /****************** |
UNION ALL
取并集后不去重。
交集
INTERSECT,MySQL
不支持。
1 | -- 两个商店中均能买到的商品 |
差集
EXCEPT,MySQL
不支持。
1 | -- 只能在A商店中买的商品 |
对称差
两个集合A B
, 求仅在A及仅在B中的元素。
1 | -- 只能在一个商店买 |
JOIN
从多个表获取信息时,用于表的合并。
使用某种关联条件将其他表中的列添加过来, 进行「添加列」的集合运算。
关联条件 — 两个表中的公共列信息(两个表建立关联的桥梁)
INNER JOIN
将两张表进行连结。其中不满足关联条件的行被丢弃。
1 | FROM <表1> INNER JOIN <表2> ON <关联条件> |
如果分组列和被聚合的列不在同一张表, 且二者都未被用于连结两张表, 则只能先连结, 再聚合。
NATURAL JOIN
INNER JOIN
的特例,会按照两个表中都包含的列名来进行等值连结。
1 | FROM <表1> NATURAL JOIN <表2> |
OUTER JOIN
将两张表进行连结。其中会保留部分不满足条件的行。提取不到的值填充为NULL
值。
- LEFT OUTER JOIN — 左外连结。保留左表(表1)中匹配失败的值。
- RIGHT OUTER JOIN — 右外连结。保留右表(表2)中匹配失败的值。
- FULL OUTER JOIN — 全外连结。两个表中匹配失败的值均会保留。
1 | FROM <表1> LEFT OUTER JOIN <表2> ON <关联条件> |
多表连结
语法中JOIN只涉及两个表,但实际使用会涉及多张表。
1 | -- INNER JOIN |
非等值连结
ON子句中的关联条件不局限于两表中相等。
交叉连结
CROSS JOIN
笛卡尔积。交叉连结不具备ON
子句(可以理解为恒真)
对两张表中的全部记录进行交叉组合,因此结果中的记录数通常是两张表中行数的乘积。
窗口函数
OnLine AnalyticalProcessing
OLAP函数。对数据库数据进行实时分析处理。
常规的SELECT语句都是对整张表进行查询,而窗口函数可以让我们有选择的去某一部分数据进行汇总、计算和排序。
1 | <窗口函数> OVER ([PARTITION BY <列名>] |
- PARTITION BY — 指定窗口对象(分组基准)
- ORDER BY — 窗口内排序
分类
窗口函数分为
- 通用函数 —
SUM MAX MIN
等 - 专用函数 —
RANK DENSE_RANK
等
通用函数在聚合函数中就在使用。专用函数是专属的。
- RANK() — 排序,存在相同位次会跳相应的位次
1 1 3
- DENSE_RANK() — 排序。存在相同位次不会跳相应的位次
1 1 2
- ROW_NUMBER() — 排序。值唯一(顺延,不存在并列)
聚合函数在窗口函数应用时,是一个累计的聚合函数值。(AVG分母从1开始加)