SQL 概述
SQL (Structured Query Language) 结构化查询语言,用于存取、查询、更新数据以及管理关系型数据库系统
SQL 发展
- SQL 是在 1981 年由 IBM 公司推出,一经推出基于其简洁的语法在数据库中得到了广泛的应用,成为主流数据库的通用规范
- SQL 由 ANSI 组织确定规范
- 在不同的数据库产品中遵守 SQL 的通用规范,但是也对 SQL 有一些不同的改进,形成了一些数据库的专有指令
- MySQ: limit
- SQLServer: top
- Oracle: rownum
SQL 分类
根据 SQL 指令完成的数据库操作的不同,可以将 SQL 指令分为四类:
- DDL Data Definition Language 数据定义语言
- 用于完成对数据库对象(数据库、数据表、视图、索引等)的创建、删除、修改
- DML Data Manipulation Language 数据操作/操纵语言
- 用于完成对数据表中的数据的添加、删除、修改操作
- 添加:将数据存储到数据表
- 删除:将数据从数据表移除
- 修改:对数据表中的数据进行修改
- DQL Data Query Language 数据查询语言
- 用于将数据表中的数据查询出来
- DCL Data Control Language 数据控制语言
- 用于完成事务管理等控制性操作
字段约束
约束介绍
在创建数据表的时候,指定的对数据表的列的数据限制性的要求(对表的列中的数据进行限制)
为什么要给表中的列添加约束?
- 保证数据的有效性
- 保证数据的完整性
- 保证数据的正确性
字段常见的约束有哪些?
- 非空约束 (not null): 限制此列的值必须提供,不能为 null
- 唯一约束 (unique): 在表中的多条数据,此列的值不能重复
- 主键约束 (primary key): 非空 + 唯一,能够唯一标识数据表中的一条数据
- 外键约束 (foreign key): 建立不同表之间的关联关系
非空约束
限制数据表中此列的值必须提供
创建表:设置图书表的 book_name not null
create table books(book_isbn char(4),book_name varchar(10) not null,book_author varchar(6));
主键约束
主键——就是数据表中记录的唯一标识,在一张表中只能有一个主键(主键可以是一个列,也可以是多个列的组合)
当一个字段声明为主键之后,添加数据时:
- 此字段数据不能为 null
- 此字段数据不能重复
创建表时添加主键约束
create table books(book_isbn char(4) primary key,book_name varchar(10) not null,book_author varchar(6));
或者
create table books(book_isbn char(4),book_name varchar(10) not null,book_author varchar(6),primary key(book_isbn));
删除表主键约束
alter table books drop primary key;
创建表之后添加主键约束
## 创建表时没有添加主键约束create table books(book_isbn char(4),book_name varchar(10) not null,book_author varchar(6));## 创建表之后添加主键约束alter table books modify book_isbn char(4) primary key;
主键自动增长
在创建一张数据表时,如果数据表中有列可以作为主键(例如:学生表的学号、图书表的 isbn)可以直接这是这个列为主键;
当有些数据表中没有合适的列作为主键时,可以额外定义一个与记录本身无关的列(ID)作为主键,此列数据无具体的含义主要⽤于标识一条记录,在 mysql 中可以将此列定义为 int,同时设置为
自动增长,当向数据表中新增一条记录时,无需提供 ID 列的值,它会自动生成
定义主键自动增长
- 定义 int 类型字段自动增长:
auto_increment
create table types(type_id int primary key auto_increment,type_name varchar(20) not null,type_remark varchar(100));
注意:自动增长从 1 开始,每添加一条记录,自动的增长的列会自定 + 1,当把某条记录删除之后再添加数据,自动增长的数据也不会重复生成(自动增长只保证唯一性、不保证连续性)
联合主键
联合组件——将数据表中的多列组合在⼀起设置为表的主键
定义联合主键
create table grades(stu_num char(8),course_id int,source int,primary key(stu_num, course_id));
注意:在实际企业项目的数据库设计中,联合主键使用频率并不高;当⼀个张数据表中没有明确的字段可以作为主键时,可以额外直接添加⼀个 ID 字段作为主键
外键约束
*在多表关联部分
DML 数据操纵语言
用于完成对数据表中数据的插入、删除、修改操作
create table students(stu_num char(8) primary key,stu_name varchar(20) not null,stu_gender char(2) not null,stu_age int not null,stu_tel char(11) not null unique,stu_qq varchar(11) unique);
插入数据
语法
insert into <tableName> (columnName, columnName...)values(value1, value2...);
示例
## 向数据表中指定的列添加数据(不允许为空的列必须提供数据)insert into stus(stu_num, stu_name, stu_gender, stu_age, stu_tel)values('20210101', '张三', '男', 21, '13030303300');## 数据表名后的字段名列表顺序可以不与表中⼀致,但是 values 中值的顺序必须与表名后字段名顺序对应insert into stus(stu_num, stu_name, stu_age, stu_tel, stu_gender)values('20210103', '王五', 20, '13030303302', '⼥');## 当向表中的所有列添加数据时,数据表名后⾯的字段列表可以省略,但是 values 中的值的顺序要与数据表定义的字段保持⼀致;insert into stus values('20210105', '孙琦', '男', 21, '13030303304', '666666');## 在项⽬开发中,即使要向所有列添加数据,也建议将列名的列表显式写出来(增强 SQL 的稳定性)insert into stus(stu_num,stu_name, stu_gender, stu_age, stu_tel, stu_qq)values('20210105', '孙琦', '男', 21, '13030303304', '666666');
删除数据
从数据表中删除满足特定条件(所有)的记录
语法
delete from <tableName> [where conditions];
示例
## 删除学号为 20210102 的学⽣信息delete from stus where stu_num = '20210102';## 删除年龄大于 20 岁的学⽣信息(如果满足 where ⼦句的记录有多条,则删除多条记录)delete from stus where stu_age > 20;## 如果删除语句没有 where ⼦句,则表示删除当前数据表中的所有记录(敏感操作)delete from stus;
修改数据
对数据表中已经添加的记录进行修改
语法
update <tableName> set columnName = value [where conditions]
示例
## 将学号为 20210105 的学⽣姓名修改为“孙七”(只修改⼀列)update stus set stu_name = '孙七'where stu_num = '20210105';## 将学号为 20210103 的学⽣性别修改为“男”,同时将 QQ 修改为 777777(修改多列)update stus set stu_gender = '男', stu_qq = '777777'where stu_num = '20210103';## 根据主键修改其他所有列update stus set stu_name = '韩梅梅', stu_gender = '⼥', stu_age=18, stu_tel = '13131313311', stu_qq = '999999'where stu_num = '20210102';## 如果 update 语句没有 where ⼦句,则表示修改当前表中所有行(记录)update stus set stu_name = 'Tom';
DQL 数据查询语言
从数据表中提取满足特定条件的记录
- 单表查询
- 多表联合查询
查询基础语法
## select 关键字后指定要显示查询到的记录的哪些列select columnName1 [, columnName2, columnName3...] from <tableName> [where conditions];## 如果要显示查询到的记录的所有列,则可以使⽤ * 替代字段名列表 (在项⽬开发中不建议使⽤ *)select * from stus;
WHERE 子句
在删除、修改及查询的语句后都可以添加 where 子句(条件),用于筛选满足特定的添加的数据进行删除、修改和查询操作
delete from tableName where conditions;update tableName set ... where conditions;select columnName... from tableName where conditions;
条件关系运算符
## = 等于select * from stus where stu_num = '20210101';## != <> 不等于select * from stus where stu_num != '20210101';select * from stus where stu_num <> '20210101';## > 大于select * from stus where stu_age > 18;## < 小于select * from stus where stu_age < 20;## >= 大于等于select * from stus where stu_age >= 20;## <= 小于等于select * from stus where stu_age <= 20;## between and 区间查询 between v1 and v2 [v1,v2]select * from stus where stu_age between 18 and 20;
条件逻辑运算符
在 where 子句中,可以将多个条件通过逻辑预算 (and or not) 进行连接,通过多个条件来筛选要操作的数据
## and 并且 筛选多个条件同时满足的记录select * from stus where stu_gender = '⼥' and stu_age < 21;## or 或者 筛选多个条件中⾄少满足⼀个条件的记录select * from stus where stu_gender = '⼥' or stu_age < 21;## not 取反select * from stus where stu_age not between 18 and 20;
LIKE 子句
在 where 子句的条件中,可以使用 like 关键字来实现模糊查询
语法
select * from tableName where columnName like 'reg';
- 在 like 关键字后的 reg 表达式中
%表示任意多个字符 【%o%包含字母 o】_表示任意一个字符 【_o%第二个字母为 o】
示例
# 查询学⽣姓名包含字⺟ o 的学⽣信息select * from stus where stu_name like '%o%';# 查询学⽣姓名第⼀个字为`张`的学⽣信息select * from stus where stu_name like '张%';# 查询学⽣姓名最后⼀个字⺟为 o 的学⽣信息select * from stus where stu_name like '%o';# 查询学⽣姓名中第⼆个字⺟为 o 的学⽣信息select * from stus where stu_name like '_o%';
对查询结果的处理
设置查询的列
声明显示查询结果的指定列
select columnName1, columnName2,... from stus where stu_age > 20;
计算列
对从数据表中查询的记录的列进⾏⼀定的运算之后显示出来
## 出⽣年份 = 当前年份 - 年龄select stu_name, 2021 - stu_age from stus;+-----------+----------------+| stu_name | 2021 - stu_age |+-----------+----------------+| 张三 | 2000 || 韩梅梅 | 2003 || 王五 | 2001 || Tom | 1988 || 孙七 | 2000 || Piolicy | 2009 |+-----------+----------------+
as 字段取别名
可以为查询结果的列名取⼀个语义性更强的别名 (as 关键字也可省略)
select stu_name, 2021 - stu_age as stu_birth_year from stus;+-----------+----------------+| stu_name | stu_birth_year |+-----------+----------------+| 张三 | 2000 || 韩梅梅 | 2003 || 王五 | 2001 || Tom | 1988 || 孙七 | 2000 || Piolicy | 2009 |+-----------+----------------+
distinct 消除重复行
从查询的结果中将重复的记录消除
distinct
select stu_age from stus;+---------+| stu_age |+---------+| 21 || 18 || 20 || 33 || 21 || 12 |+---------+select distinct stu_age from stus;+---------+| stu_age |+---------+| 21 || 18 || 20 || 33 || 12 |+---------+
排序 - order by
将查询到的满足条件的记录按照指定的列的值升序/降序排列
语法
select * from tableName where conditions order by columnName asc|desc;
- order by columnName 表示将查询结果按照指定的列排序
- asc 按照指定的列升序(默认)
- desc 按照指定的列降序
实例
## 单字段排序select * from stus where stu_age > 15 order by stu_gender desc;+----------+-----------+------------+---------+-------------+--------+| stu_num | stu_name | stu_gender | stu_age | stu_tel | stu_qq |+----------+-----------+------------+---------+-------------+--------+| 20210101 | 张三 | 男 | 21 | 13030303300 | NULL || 20210103 | 王五 | 男 | 20 | 13030303302 | 777777 || 20210104 | Tom | 男 | 33 | 13030030220 | 333433 || 20210105 | 孙七 | 男 | 21 | 13030303304 | 666666 || 20210102 | 韩梅梅 | ⼥ | 18 | 13131313311 | 999999 |+----------+-----------+------------+---------+-------------+--------+## 多字段排序:先满足第⼀个排序规则,当第⼀个排序的列的值相同时再按照第⼆个列的规则排序select * from stus where stu_age > 15 order by stu_gender asc, stu_age desc;+----------+-----------+------------+---------+-------------+--------+| stu_num | stu_name | stu_gender | stu_age | stu_tel | stu_qq |+----------+-----------+------------+---------+-------------+--------+| 20210102 | 韩梅梅 | ⼥ | 18 | 13131313311 | 999999 || 20210104 | Tom | 男 | 33 | 13030030220 | 333433 || 20210101 | 张三 | 男 | 21 | 13030303300 | NULL || 20210105 | 孙七 | 男 | 21 | 13030303304 | 666666 || 20210103 | 王五 | 男 | 20 | 13030303302 | 777777 |+----------+-----------+------------+---------+-------------+--------+
聚合函数
SQL 中提供了⼀些可以对查询的记录的列进行计算的函数——聚合函数
- count
- max
- min
- sum
- avg
count()统计函数,统计满足条件的指定字段值的个数(记录数)
## 统计学⽣表中学⽣总数select count(stu_num) from stus;+----------------+| count(stu_num) |+----------------+| 6 |+----------------+## 统计学⽣表中性别为男的学⽣总数select count(stu_num) from stus where stu_gender = '男';+----------------+| count(stu_num) |+----------------+| 4 |+----------------+
max()计算最大值,查询满足条件的记录中指定列的最大值
select max(stu_age) from stus;+--------------+| max(stu_age) |+--------------+| 33 |+--------------+select max(stu_age) from stus where stu_gender = '⼥';+--------------+| max(stu_age) |+--------------+| 18 |+--------------+
min()计算最小值,查询满足条件的记录中指定列的最小值
select min(stu_age) from stus;+--------------+| min(stu_age) |+--------------+| 12 |+--------------+select min(stu_age) from stus where stu_gender = '⼥';+--------------+| min(stu_age) |+--------------+| 12 |+--------------+
sum()计算和,查询满足条件的记录中指定的列的值的总和
## 计算所有学⽣年龄的总和select sum(stu_age) from stus;+--------------+| sum(stu_age) |+--------------+| 125 |+--------------+## 计算所有性别为男的学⽣的年龄的总和select sum(stu_age) from stus where stu_gender = '男';+--------------+| sum(stu_age) |+--------------+| 95 |+--------------+
avg()求平均值,查询满足条件的记录中 计算指定列的平均值
select avg(stu_age) from stus;+--------------+| avg(stu_age) |+--------------+| 20.8333 |+--------------+select avg(stu_age) from stus where stu_gender = '男';+--------------+| avg(stu_age) |+--------------+| 23.7500 |+--------------+
日期函数和字符串函数
- 日期函数
当向日期类型的列添加数据时,可以通过字符串类型赋值(字符串的格式必须为yyyy-MM-dd hh:mm:ss)
如果我们想要获取当前系统时间添加到日期类型的列,可以使⽤
now()或者sysdate()
示例
## 通过字符串类型给⽇期类型的列赋值alter table stus add stu_entrance datetime;insert into stus(stu_num, stu_name, stu_gender, stu_age, stu_tel, stu_qq, stu_entrance)values('20210107', '七七', '女', 20, '13333333333', '12333333', '2021-10-25 22:34:28');## 通过 now() 获取当前时间insert into stus(stu_num,stu_name,stu_gender,stu_age,stu_tel,stu_qq,stu_entrance)values('20210109','张⼩四','⼥',20,'13434343355','1233333',now());## 通过 sysdate() 获取当前时间insert into stus(stu_num,stu_name,stu_gender,stu_age,stu_tel,stu_qq,stu_entrance)values('20210110', '李雷', '男', 16, '13434343366', '123333344', sysdate());## 通过 now() 和 sysdate() 获取当前系统时间select now();+---------------------+| now() |+---------------------+| 2021-10-25 22:43:51 |+---------------------+select sysdate();+---------------------+| sysdate() |+---------------------+| 2021-10-25 22:44:12 |+---------------------+
- 字符串函数
就是通过 SQL 指令对字符串进行处理
示例
## concat(column1, column2, ...) 拼接多列select concat(stu_name,'-',stu_gender) from stus;+---------------------------------+| concat(stu_name,'-',stu_gender) |+---------------------------------+| 张三-男 || 韩梅梅-⼥ || 王五-男 || Tom-男 || 孙七-男 || Policy-女 || 七七-女 |+---------------------------------+## upper(column) 将字段的值转换成⼤写select upper(stu_name) from stus;+-----------------+| upper(stu_name) |+-----------------+| 张三 || 韩梅梅 || 王五 || TOM || 孙七 || POLICY || 七七 |+-----------------+## lower(column) 将指定列的值转换成⼩写select lower(stu_name) from stus;+-----------------+| lower(stu_name) |+-----------------+| 张三 || 韩梅梅 || 王五 || tom || 孙七 || policy || 七七 |+-----------------+## substring(column, start, len) 从指定列中截取部分显示 (start 从 1 开始,后数 lun 个字符)select stu_name, substring(stu_tel, 8, 4) from stus;+-----------+--------------------------+| stu_name | substring(stu_tel, 8, 4) |+-----------+--------------------------+| 张三 | 3300 || 韩梅梅 | 3311 || 王五 | 3302 || Tom | 0220 || 孙七 | 3304 || Policy | 3789 || 七七 | 3333 |+-----------+--------------------------+
分组查询 - group by
分组——就是将数据表中的记录按照指定的列进行分组
语法
select 分组字段/聚合函数from 表名[where 条件]group by 分组列名 [having 条件][order by 排序字段]
select后使用 * 显示对查询的结果进行分组之后,显示每组的第一条记录(这种显示通常是无意义的)select后通常显示分组字段和聚合函数 (对分组后的数据进行统计、求和、平均值等)- 语句执行属性:
- 先根据 where 条件从数据库查询记录
- group by 对查询记录进行分组
- 执行 having 对分组后的数据进行筛选
示例
# 先对查询的学生信息按性别进行分组(分成了男、⼥两组),然后再分别统计每组学生的个数select stu_gender, count(stu_num) from stus group by stu_gender;+------------+----------------+| stu_gender | count(stu_num) |+------------+----------------+| 男 | 4 || ⼥ | 3 |+------------+----------------+# 先对查询的学生信息按性别进行分组(分成了男、⼥两组),然后再计算每组的平均年龄select stu_gender, avg(stu_age) from stus group by stu_gender;+------------+--------------+| stu_gender | avg(stu_age) |+------------+--------------+| 男 | 23.7500 || ⼥ | 16.6667 |+------------+--------------+# 先对学生按年龄进行分组(分了16、17、18、20、21、22六组),然后统计各组的学生数量,还可以对最终的结果排序select stu_age, count(stu_num) from stus group by stu_age order by stu_age;+---------+----------------+| stu_age | count(stu_num) |+---------+----------------+| 12 | 1 || 18 | 1 || 20 | 2 || 21 | 2 || 33 | 1 |+---------+----------------+# 查询所有学生,按年龄进行分组,然后分别统计每组的⼈数,再筛选当前组⼈数 > 1 的组,再按年龄升序显示出来select stu_age, count(stu_num) from stusgroup by stu_age having count(stu_num) > 1order by stu_age;+---------+----------------+| stu_age | count(stu_num) |+---------+----------------+| 20 | 2 || 21 | 2 |+---------+----------------+# 查询性别为'男'的学生,按年龄进行分组,然后分别统计每组的⼈数,再筛选当前组⼈数 > 1 的组,再按年龄升序显示出来select stu_age, count(stu_num) from stuswhere stu_gender = '男'group by stu_age having count(stu_num) > 1order by stu_age;+---------+----------------+| stu_age | count(stu_num) |+---------+----------------+| 21 | 2 |+---------+----------------+
分页查询 - limit
当数据表中的记录比较多的时候,如果⼀次性全部查询出来显示给用户,用户的可读性/体验性就不太好,因此可以将这些数据分页进行展示
语法
select ...from ...where ...limit param1, param2
- param1 int, 表示获取查询语句的结果中的第一条数据的索引(索引从 0 开始)
- param2 int, 表示获取的查询记录的条数(如果剩下的数据条数少于 param2,则返回剩下的所有记录)
示例
对数据表中的学生信息进行分页显示,总共有 10 条数据,每页显示 3 条
总记录数 count 10
每页显示 pageSize 3
总页数 pageCount = count % pageSize==0 ? count / pageSize : count / pageSize +1;
# 查询第⼀⻚:select * from stus [where ...] limit 0, 3; (1 - 1) * 3# 查询第⼆⻚:select * from stus [where ...] limit 3, 3; (2 - 1) * 3# 查询第三⻚:select * from stus [where ...] limit 6, 3; (3 - 1) * 3# 如果在⼀张数据表中:# pageNum 查询的⻚码# pageSize 每⻚显示的条数# 通⽤分⻚语句如下:select * from <tableName> [where ...] limit (pageNum - 1) * pageSize, pageSize;
