文章目录
- 结构化查询语言:SQL
- 数据定义语言:DDL
-
- [数据库 DDL](#数据库 DDL)
- [数据表 DDL](#数据表 DDL)
- 数据操作语言:DML
- 数据查询语言:DQL
结构化查询语言:SQL
SQL(Structured Query Language,结构化查询语言)是一种用于管理关系数据库系统的特定语言。通过 SQL 可以定义、操作和管理数据库中的数据,执行各种数据库操作。SQL 主要由数据定义语言(DDL)、数据操纵语言(DML)、数据控制语言(DCL)和事务控制语言(TCL)组成,用于定义数据库结构、操纵数据、控制访问权限以及管理事务。
SQL 适用于所有关系型数据库。MySQL、Oracle、SQLServer 是一个数据库软件,这些数据库软件支持标准 SQL,不过每一个数据库系统会在标准 SQL 的基础上扩展自己的 SQL 语法。大部分的 NoSQL 数据库有自己的操作语言,对 SQL 支持的并不好。
SQL 语法关键字大小写不敏感、通过 -- 写法可以添加注释信息。目前,SQL-92 是被广泛接受和应用的一个基本 SQL 标准版本。SQL 常见版本有:
| SQL 版本 | 发布年份 | 主要特性 |
|---|---|---|
| SQL-86 | 1986 年 | 基本的 SQL 标准 |
| SQL-89/92 | 1989/1992 年 | 在 SQL-86 基础上扩展,增加额外功能 |
| SQL-92 | 1992 年 | 连接操作、子查询、触发器等重要功能 |
| SQL:1999 | 1999 年 | 窗口函数、递归查询等新功能 |
| SQL:2003 | 2003 年 | XML 支持、标量数据类型、增强连接操作等功能 |
| SQL:2006 | 2006 年 | 根据 SQL:2003 进行修订和改进 |
| SQL:2011 | 2011 年 | 时间区域、分析函数等新增功能 |
| SQL:2016 | 2016 年 | JSON 支持和其他改进 |
SQL 注入的原理是用户输入的内容作为了 SQL 语句语法的一部分,改变了原有 SQL 真正的意义,如:
sql
select * from 表名 where username=用户传递的数据1 and password = 用户传递的数据2
此时,用户传入账号和密码。但是如果用户进行 sql 注入,用户传递的数据 2 为:
sql
用户传递的数据2 or 1=1
通过拼接 SQL 语句会变成:
sql
select * from 表名 where username=用户传递的数据1 and password = 用户传递的数据2 or 1=1
此时,不论用户传递的数据是否正确,都能查询出结果。
数据定义语言:DDL
DDL(Data Definition Language)即数据定义语言,是被用于定义数据库对象(包含数据库、表、字段)的语言。
数据库 DDL
关于 MySQL 数据库,常用的 DDL 操作命令有:
| 命令 | 说明 |
|---|---|
| show databases; | 展示所有库 |
create database [if not exists] 数据库名 [charset=utf8] |
创建库 |
use 数据库名; |
使用库 |
drop database [if exists] 数据库名; |
删除库 |
alter database 数据库名 character set utf8; |
设置库编码 |
| select database(); | 查看当前库 |
数据表 DDL
创建数据表模板:
sql
-- 创建数据表模板
create table [if not exists] `表名`(
`字段名1` `类型[(宽度)]` `[约束条件]` [comment `字段说明`],
`字段名2` `类型[(宽度)]` `[约束条件]` [comment `字段说明`],
`字段名3` `类型[(宽度)]` `[约束条件]` [comment `字段说明`]
)[表的一些设置];
数据表操作命令:
| 数据表操作命令 | 说明 |
|---|---|
| show tables; | 查看当前库所有表 |
show create table 表名; |
查看指定表的建表SQL |
desc 表名 |
查看表结构 |
drop table 表名 |
删除表 |
alter table 表名 add 列名 类型(长度) [约束]; |
添加列 |
alter table 表名 change 旧列名 新列名 类型(长度) 约束; |
修改列名与类型 |
alter table 表名 drop 列名; |
修改表删除列 |
rename table 表名 to 新表名; |
修改表名 |
表复制操作:
-
insert into select 语句复制表(要求目标表 table2 必须存在):
sqlinsert into table2(field1,field2,...) select value1,value2,... from table1; insert into table2 select * from table1; -
select into from 语句复制表(要求目标表 table2 必须不存在):
sqlselect value1, value2 into table2 from table1;
数据操作语言:DML
DML(Data Manipulation Language)即数据操作语言,用于对数据表中的数据进行增删改。
| SQL | 说明 |
|---|---|
insert into 表名 [(列名1,列名2,列名3...)] values (值1,值2,值3...); |
向表中某些列插入(不加列名则插入所有列)数据 |
load data infile "文件路径" into table 表名 fields terminated by '分隔符' |
向表中导入文本文档数据 |
update 表名 set 列名1=值1,列名2=值2... [where 条件]; |
向表中更新数据 |
delete from 表名 [where 条件]; |
根据条件删除数据(不加 where 条件则删除整张表) |
truncate table 表名 |
truncate 表名 |
数据查询语言:DQL
DQL(Data Query Language)即数据查询语言,用于对数据库表中的数据进行查询。DQL 语法格式:
sql
select
[all|distinct]
`<目标列的表达式1>` `[别名]`,`<目标列的表达式2>` `[别名]`,...
from `<表名|视图名>` `[别名]`,`<表名 | 视图名>` `[别名]`,...
[where `<条件表达式>`]
[group by `<列名>`
[having `<条件表达式>`]
[order by `<列名>` [asc|desc]
[limit `<数字或者列表>`];
DQL 的执行顺序与 DQL 的编写顺序并不相同,DQL 的执行顺序为:
html
from -> where -> group by -> having -> select -> order by -> limit
条件查询
条件查询通过 WHERE 关键字后加入具体条件来实现。条件查询与运算符密切相关,比较运算符与逻辑运算符是两大常用运算符。
常用的比较运算符:
| 比较运算符关键字 | 描述 |
|---|---|
| = | 等于 |
| <> 或 != | 不等于 |
| < | 小于 |
| > | 大于 |
| <= | 小于等于 |
| >= | 大于等于 |
| like | 模糊匹配 |
| in(...) | 判断某个字符串是否在给定的集合中 |
| is null | 判断值是否为空(NULL) |
| is not null | 判断值是否不为空 |
| between ... and ... | 判断值是否在指定范围之间 |
| not between ... and ... | 判断值是否不在指定范围之间 |
在通过 LIKE 关键字进行模糊查询时,需要结合模糊匹配规则共同使用,从而达到模糊查询的目的。模糊匹配的规则有:
%:放在具体条件值前后,匹配任意字符序列(包括空字符串)_:放在具体条件值前后,匹配任意单个字符。
常用的逻辑运算符:
| 逻辑运算符关键字 | 描述 |
|---|---|
| and 或 && | 与运算,表示所有条件都必须为真 |
| or 或 || | 或运算,表示其中至少一个条件为真 |
| not 或 ! | 非运算,表示条件为假 |
排序查询
排序查询语法:
sql
select 字段名,... from 表名 order by 字段1 [asc|desc], 字段2 [asc|desc], ...;
asc、desc 分别表示排序规则为升序、降序。如果排序为多字段排序,那么当第一个字段值相同时,才会根据第二个字段进行排序。
聚合查询
聚合查询的本质是通过聚合函数将一列数据作为一个整理,从而进行纵向计算。常用的聚合函数:
| 聚合函数 | 说明 |
|---|---|
COUNT |
求列非空值行数 |
SUM |
求列值总和 |
AVG |
求列平均值 |
MAX |
求列最大值 |
MIN |
求列最小值 |
count(*)、count(1)、count(column) 的区别:
COUNT(*):统计表中所有行的数量,包括 NULL 值与重复值。当使用count(*)时,数据库会遍历表中的每一行,对每一行都进行计数,不管该行的值是否为空。这意味着数据库需要考虑表中的所有列,因为*代表所有列。因此,这种方式可能会略微慢一些,因为需要考虑所有列的情况。COUNT(1):统计表中所有行的数量,包括 NULL 值与重复值。当使用count(1)时,实际上并不会涉及对表中的列进行操作。这是因为count(1)会简单地检查每一行是否存在,不关心具体的列值是多少,只要存在就加一。因此,它的计算方式比较简单,性能相对更高一些。COUNT(column_name):会统计指定列中非 NULL 值的数量。
分组查询
WHERE 与 HAVING 的区别主要有两点:
WHERE在分组之前进行过滤,不满足WHERE条件的数据将不参与分组,而HAVING是对分组后的结果进行过滤;WHERE不能对聚合函数进行判断,而HAVING可以。
分组后查询的字段一般为聚合函数与分组字段,查询其他字段无任何意义。
分组查询语法:
sql
select 字段名 from 表名 [where 条件] group by 分组字段名 having 分组后过滤条件;
分组查询的使用案例:
sql
-- 根据性别分组,统计男性员工与女性员工的数量:
select gender,count(*) from emp group by gender;
-- 根据性别分组,统计男性员工与女性员工的平均年龄:
select gender,avg(age) from emp group by gender;
-- 查询年龄小于45的员工,并根据工作地址分组,获取员工数量大于等于3的工作地址
select workaddress,count(*) address_count from emp where age<45 group by workaddress having address_count >= 3;
分页查询
分页查询语法:
sql
select 字段名, ... from 表名 [where 条件] order by 排序字段名 [asc|desc], ... limit 起始索引, 查询记录数;
起始索引从 0 开始,起始索引的计算规则为:
html
起始索引=(查询页码-1)*每页记录数
分页查询是数据库方言,不同数据库有不同实现,在 MySQL 中为 Limit。如果分页查询的是第一页数据,起始索引可忽略(简写为 Limit 查询记录数)。
连接查询
表与表之间的关系主要分为一对一、一对多、多对多关系。除了基于上述三种关系下的多表查询,还存在一种交叉连接查询(也被称为笛卡尔积查询)的查询方式。
笛卡尔积(交叉连接)查询的定义与理解
笛卡尔积(交叉连接)查询是将两表间的每一行都进行组合,生成一个新表,新表的行数等于两个表的行数的乘积。但是交叉连接查询在实际应用中使用很少,因为它会生成非常大的结果集,并且没有连接条件,难于筛选出需要的数据。所以通常情况下,会使用内连接、外连接或子查询等方式进行多表间的数据查询。
内连接查询
内连接查询两张表交集部分的数据,分为隐式内连接与显式内连接。
sql
select 字段名,... from 表1 [inner] join 表2 on 连接条件...; -- 显式内连接
select 字段名,... from 表1,表2 where 条件...; -- 隐式内连接
外连接查询
外连接查询以一张表所有数据为基础,与另一张交集部分的数据,分为左外连接与右外连接。
-
左外连接:查询左表所有数据,包含左表与右表交集部分数据。
sqlselect 字段名,... from 表1 left [outer] join 表2 on 条件...; -
右外连接:查询右表所有数据,包含左表与右表交集部分数据。
sqlselect 字段名,... from 表1 right [outer] join 表2 on 条件...;
自连接查询
自连接查询是指在同一张表中进行连接操作,自连接可以是内连接,也可以是外连接。
比如通过内连接方式的自连接,查询员工及其领导名称:
sql
select e.employee_id, e.employee_name, m.employee_name as manager_name
from employees e
join employees m on e.manager_id = m.employee_id;
上例中,对于 manager_id 为 NULL 的数据,并不会查询出来,因为内连接查询的是两表的交集。为解决该问题可以通过外连接方式的自连接。
联合查询
联合查询将多次查询的结果合并起来,形成一个新结果集。联合查询语法:
sql
select 字段名,... from 表A ...
union [all]
select 字段名,... from 表B ...
UNION 与 UNION ALL 的区别在于,UNION 会将结果进行去重后再进行合并,而 UNION ALL 会直接将查询结果合并(不会进行去重)。
对于联合查询的多张表,要求列数与字段类型必须都要一致。
子查询
子查询是指在一个查询语句中嵌套另一个查询语句。子查询语法:
sql
select 字段名,... from 表1 where 字段名 in (select 字段名 from 表2 where 条件);
子查询外部的语句可以是 INSERT/UPDATE/DELETE/SELECT 中的任何一个。
根据子查询的结果不同,可以将子查询分为 4 类:
| 子查询类型 | 说明 | 常用操作符 |
|---|---|---|
| 标量子查询 | 子查询结果为单个值(数字、字符串、日期等) | =、<>、>、>=、<、<= |
| 列子查询 | 子查询结果为一列 | IN、NOT IN、ANY、SOME、ALL |
| 行子查询 | 子查询结果为一行 | =、<>、IN、NOT IN |
| 表子查询 | 子查询结果为多行多列 |
| 操作符 | 说明 |
|---|---|
| IN | 在指定集合范围内 |
| NOT IN | 不在指定集合范围内 |
| ANY、SOME | 满足集合范围内任意一个元素即可 |
| ALL | 必须满足集合范围内所有元素 |
标量子查询案例(查询晚于 "东方白" 入职的员工信息):
sql
select * from emp where entrydate > (select entrydate from emp where name = '东方白');
列子查询案例(查询高于 "财务部" 所有人员工资的员工信息):
sql
select * from emp where salary > all(select salary from emp where dept_id = (select id from dept where name = '财务部'));
行子查询案例(查询与 "张无忌" 薪资和直属领导相同的员工信息):
sql
select * from emp where (salary,manageid) = (select salary,manageid from emp where name = '张无忌');
表子查询案例(查询入职日期是 "2024-03-16" 之后的员工信息与部门信息):
sql
select * from (select * from emp where entrydate > '2024-03-16') e left join dept d on e.dept_id = d.id;