数据库SQL语句
# SQL语句介绍
- SQL即是结构化查询语言(Structured Query Language),它是关系型数据库通用的语言,遵循SQL92标准 (SQL_MODE)。
- 它用于操作或查询数据库内部对象(库、表等)。实现同一功能时,查询速度会由语句决定。
# SQL语句分类
- DDL语句 - 数据定义语言(Data Definition Language)
- DQL语句 - 数据查询语言(Data Query Language)
- DCL语句 - 数据控制语言(Data Control Language)
- DML语句 - 数据操作语言(Data Manipulation Language)
# DDL语句相关概念
DDL语句用于库与表的定义、修改等操作,所以我们应该先了解库表以及相关概念。
# 库表逻辑结构
- 库
- 库名称
- 库属性:字符集、排序规则
- 表
- 表名
- 表属性:存储引擎类型、字符集、排序规则
- 列名
- 列属性:数据类型、约束、其他属性
- 数据行 (记录)
# 库表定义规范
# 建库规范
- 数据库名使用小写字母字符。
- 数据库名不能以数字开头,且不能是数据库内部的关键字,例如: table。
- 建数据库必须要设置字符集,例如:utf8mb4。
- 不要直接通过Linux目录创建数据库,没有语法检查。
# 建表规范
- 表达含义为"是/否"的字段,须使用is_xxx的方式命名。比如表达逻辑删除的字段名is_deleted,1表示删除,0表示未删除。
- 表名、字段名必须使用小写字母或数字,不能以数字开头,不能出现两个下划线中间只有数字,且不能是数据库内部的关键字,例如: table。
- 数据库字段名的修改代价很大,因为会锁表,所以字段名称需要慎重考虑。
- 正例:aliyun_admin、rdc_config、level3_name
- 反例:AliyunAdmin、rdcConfig、level_3_name
- 主键索引名应该以
pk_
开头、唯一索引名以uk_
开头、普通索引名以idx_
开头。 - 小数类型应使用decimal,禁止使用float和double。
- 在存储的时候,float和double都存在精度损失的问题,很可能在比较值的时候,得到不正确的结果。如果存储的数据范围超过decimal的范围,建议将数据拆成整数和小数并分开存储。
- 表的命名最好是遵循"业务名称_表的作用"。
# 字符集
- 后端程序想要正常存储或者获取数据库数据,就需要正确指定字符集,不然可能会乱码。
- MySQL默认字符集:8.0之前是拉丁语latin1、8.0之后是utf8mb4。
- 查看mysql字符集语句:
show charset;
- 常用字符集:
utf8
- 中文是3字节。utf8mb4
- 中文是4字节,但建议使用,它比utf8更完善(包含utf8),且支持emoji等字符。
# 排序规则
- 用于决定数据是否大小写敏感之类的,排序规则需要对应字符集。
- 查看排序规则语句:
show collation;
- 常用排序规则格式:
[字符集]_general_ci
- 大小写不敏感[字符集]_bin
- 大小写敏感,可以存拼音、日文等
# 数据类型
MySQL中数据类型的选择会影响到索引的性能,选择正确的数据类型可以有效提高数据的存储效率和读写效率。MySQL中的数据类型大概可以分为数字类型、时间类型、字符串类型、二进制类型等。
# 整数类型
# 浮点数类型
#
decimal
- 自定义精度浮点数- 定义格式:
DECIMAL(P,D)
,P是数字的最长个数(范围1~65),D是小数位数的最长个数(范围0~30)。 - 例如:
DECIMAL(6,2)
,最多可以存储6位数字,小数位数为2位,因此它的范围是从-9999.99到9999.99。
- 定义格式:
#
FLOAT
- 单精浮点数(4个字节)#
DOUBLE
- 双精浮点数(8个字节)
# 字符串类型
#
CHAR
- 定长字符串类型- 需要指定字符长度,最长支持255个字符,例如:CHAR(255)。
- 不管存储的字符串长度多长,都会立即分配指定字符长度的存储空间,未填满的空间以空格填充。
#
VARCHAR
- 变长字符串类型- 需要指定最大字符数,最长支持65533个字符,例如:VARCHAR(1024)。
- 每次存储数据之前,都会先判断一下字符长度,然后按需要申请存储空间。每255个字符就会新申请一个字符空间来存储字符,最少会占用一个字符空间,也就是255字符。
- 预留和实际占用字节说明
- VARCHAR(20)表示最大存储20个字符,无论是中文、英文、数字等,都是最多20个。
- 无论是中文、英文、数字等,占用的预留字节长度都是4字节,但物理磁盘上实际占用的只是中文4字节,英文和数字1字节。
#
ENUM
- 枚举类型- 需要指定固定的字符串数据,如ENUM('sisa','cn'...),然后存储数据时,只能使用这其中的字符串数据。
- 且它会给字符串数组的值,对应一个个索引1、2、3等。
# 时间类型
#
DATE
- 日期类型- 格式:YYYY-MM-DD
#
TIME
- 时间类型- 格式:hh:mm:ss[.uuuuuu]
#
DATETIME
- 日期时间类型- 格式:YYYY-MM-DD hh:mm:ss[.uuuuuu]
#
TIMESTAMP
- 时间戳类型- 即便时区不同,时间戳也是不变的。因此它以日期时间格式显示时,会随时区自动变更日期时间。
# 列属性约束
列属性即是列约束,保证表的结构和数据的正确性和稳定性。
PRIMARY KEY
- 主键约束- 一张表只能有一个主键列,主键列的值必须是非空且唯一的,因此需要同时配置NOT NULL。
- 可以直接在定义列的地方进行定义。也可以在定义索引的地方进行定义,例如:PRIMARY KEY(id)。
- 如果单独一个列无法满足唯一条件,则可以使用联合主键,用多个列构建成一个主键,例如:
PRIMARY KEY(email,name)
。
UNIQUE KEY
- 唯一键约束- 不允许重复的值,但可以为空。
- 可以直接在定义列的地方进行定义。也可以在定义索引的地方进行定义,例如:
UNIQUE KEY(id)
。
AUTO_INCREMENT
- 自增长约束- 如果插入数据时没有指定该列,则该列的值会进行自增长,用于数字列。
UNSIGNED
- 无符号约束- 非负数数字列,会将本来用于存放负数的空间拿来存放正数,以增加可存储的正数数值范围。
- 定义时定义在类型后面。
NOT NULL
- 非空约束- 表示该列不允许空值
DEFAULT
- 默认值- 指定默认值,例如:
DEFAULT "张三"
。 - 一般配合NOT NULL一起使用。
- 指定默认值,例如:
COMMENT
- 注释- 备注以方便后续查看字段含义,例如:
COMMENT "用户ID"
。
- 备注以方便后续查看字段含义,例如:
# 例子
CREATE TABLE student(
`id` int UNSIGNED PRIMARY KEY AUTO_INCREMENT,
`name` char(48) NOT NULL,
`age` tinyint NOT NULL,
`gender` enum("male", "female", "unknown") DEFAULT "unknown"
)ENGINE=innodb CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
2
3
4
5
6
# DDL语句
# 库相关操作
# 创建数据库
create database [数据库名];
create database [数据库名] charset [字符集] collate [排序规则];
- 默认是拉丁语字符集,最好设为utf8mb4。
# 删除数据库
drop database [数据库名];
生产环境中不要使用,一定要注意自己正在操作是测试环境还是生产环境。
# 修改数据库
alter database [数据库名] charset [字符集] collate [排序规则];
修改系统字符集需要注意,目标字符集一定要是原字符集的严格超集。
- 比如:从utf-8修改到utf8mb4。
# 表列相关操作
使用DDL语句修改或添加列会导致锁表,表的数据量越大,锁的时间越长,只能查不能修改,所以要谨慎操作,且不要在业务繁忙的时候直接操作。
# 创建表
建表格式:
create table [表名] ( [列名] [列数据类型] [列属性] comment [注释], [列名] [列数据类型] [列属性] comment [注释], ... )engine [表存储引擎] charset [表字符集];
1
2
3
4
5例子:
create table stu ( `id` int primary key not null auto_increment comment '学号', `name` char(255) not null comment '姓名', `age` tinyint unsigned not null default 0 comment '年龄', `gender` enum('m','f','n') not null default 'n' comment '性别', `create_time` datetime not null default now() comment '入学时间' )engine innodb charset utf8mb4 comment '注释';
1
2
3
4
5
6
7
# 复制表
create table [新表名] like [源表名];
# 删除表
drop table [表名];
危险操作,谨慎的使用
# 修改表名
alter table [表名] rename to [新表名];
# 修改表属性
alter table user_base [属性信息];
例如:
alter table user_base ENGINE InnoDB;
# 在表的最后添加列
alter table [表名] add [列名] [列数据类型] [列属性] comment [注释];
add后面和定义时一样。
# 在指定列后面添加列
alter table [表名] add [列名] [列数据类型] [列属性] comment [注释] after [指定列];
# 在表的第一列添加列
alter table [表名] add [列名] [列数据类型] [列属性] comment [注释] first;
# 删除指定列
alter table [表名] drop [列名];
删除会造成锁表,并且数据会全部删除,所以需要谨慎操作。
# 修改列的属性
alter table [表名] modify [列名] [列数据类型] [列属性] comment [注释];
修改必须写全,无法只修改某信息。
# 修改列名和属性
alter table [表名] change [原列名] [目标列名] [列数据类型] [列属性] comment [注释];
# DCL语句
用于数据用户的权限管理等。
# 相关概念
# MySQL权限
所有权限:
all
- 除了grant option外的所有权限,root用户的权限即是all + grant option。
授权命令权限:
grant option
其他权限:
select,insert, update, delete, create, drop, reload, shutdown, process, file, references, index, alter, show databases, super, create temporary tables, lock tables, execute, replication slave, replication client, create view, show view, create routine, alter routine, create user, event, trigger, create tablespace
# 作用目标
[数据库名].[表]
- 授权指定数据库的指定表[数据库名].*
- 授权指定数据库的所有表,一般用于开发或程序*.*
- 授权所有库的所有表,一般用于管理员
# 主机域
用户名@'主机域(白名单)'
- 用于定义允许登录的主机。# 常见写法
'%'
- 表示匹配任何地址。'localhost'
、``'127.0.0.1'` - 表示匹配本地地址。'10.0.0.%'
、'10.0.0.5%'
、'10.0.%.%'
- 通配符匹配。'10.0.0.0/255.255.255.0'
- 网段匹配。
# 授权权限
grant [权限] on [作用目标] to [用户名]@'[主机域]';
- 一般仅会授权需要的权限,比如:SELECT,INSERT,UPDATE,DELETE。
grant [权限] on [作用目标] to [用户名]@'[主机域]' identified by '[密码]';
,创建账号并授权,8.0版本之前可用。
# 回收权限
revoke [权限] on [作用目标] from [用户名]@'[主机域]';
# DML语句
用于添加、修改、删除表的记录。
# 插入数据
写法一
insert [表名] values(值);
- 值需要根据表中列名的顺序填写,默认值需要指定default。
- 例如:
insert into stu values(1,'hh',16,default,now());
写法二
insert into [表名](列名1,列名2...) values (值1,值2...);
- 值需要根据指定列名的顺序填写,有默认值可以不指定。
插入多行
insert into [表名](列名) values(值),(值),(值)...;
从SELECT获取数据插入
- 例如:
insert into stu1(name,age) select name,age form stu2 where xxx=xxx;
- 例如:
数据插入规范
- mysql中insert语句的into可以省略。
- 字符串要加引号,数字不用加,值用逗号","分开。
- 自增的数字列,可以不指定值。有默认值的列,也可以不指定值。
# 修改数据
update [表名] set [列名]='[修改值]',[列名]='[修改值]'... where [条件];
# 例如
update stu set name='john' where id=2;
# 注意
- update语句必须要加where条件,如果不加,就是修改列的所有记录。
- 可以一次性修改多个列的值,设置语句中间用逗号","隔开。
- 可以将列值修改为默认值,设置语句的值指定为default即可,例如:
age=default
。
# 删除数据
delete from [表名] where [条件];
通过where的条件来指定要删除的一条或多条数据。
# 注意
- 危险语句需要谨慎操作,delete语句必须要加where条件,如果不加,就是删除表中的所有数据。
# DQL语句
DQL语句用于查询数据库系统中的参数、库、表或记录。
# 查询库
# 查询表
一般会先切换到目标库中
use [数据库名];
。# 查询库中表名
show tables;
# 查询建表语句
show create table [表名];
# 查询表的列信息
desc [表名];
# 查询数据行
select * from [表名];
*
表示展示所有列。
# 查询系统参数
select @@[参数];
- 查询指定参数show variables like 'base%';
- 模糊查询参数- 常见参数:
select @@port;
、select @@basedir;
、select @@server_id;
# 查询函数
select 函数名();
- 调用指定函数- 常见参数:
select now();
、select database();
、select user();
# 查询用户权限
show grants for [用户名]@'[主机域]';
# SELECT语句
SELECT语句属于DQL语句,一般用于查询表数据,它支持通过各种子句来实现复杂的查询。
# SELECT语法
注意:
- SELECT用于指定结果要展示的列,FROM用于指定要查询的表。
- 除了SELECT和FROM是固定语句外,其他都可以单独使用或者直接不使用。但所有用到的子句必须按以下排序书写。
- GROUP BY后才会调用SELECT。
SELECT [列名] FROM [表名] WHERE [条件] GROUP BY [分组列] HAVING [条件] ORDER BY [排序列] LIMIT [限制展示的行]
1
2
3
4
5
6
# WHERE子句
SELECT [列名] FROM [表名] WHERE [条件];
WHERE子句用于指定查看或操作的筛选条件。
# 条件
WHERE [列名]=[值];
- 等值条件查询WHERE [列名]>[值];
- 比较条件查询,就是>、<、>=、<=
这些条件。WHERE [列名] NOT IN ('[值1]',[值2]);
、WHERE [列名]!=[值];
- 不等值查询,不走索引,尽量不要使用
# 逻辑连接符
OR
- 或,例如:SELECT [显示列] FROM [表名] WHERE [条件1] OR [条件2];
- 同一字段的多个OR逻辑,应当使用括号括起来。
AND
- 与(并且),例如:SELECT [显示列] FROM [表名] WHERE [条件1] AND [条件2];
# like子句
SELECT [显示列] FROM [表名] WHERE [判断参考列] LIKE '[模糊匹配内容]';
- like子句可以通过通配符,实现模糊匹配条件查询。
- 例如:
SELECT * FROM city WHERE countrycode LIKE 'CH%';
- 注意:
- 不要在前面加%号,因为是不走索引的性能极差。
- 另外即便是匹配数值类型的字段,也需要加引号。
# in子句
SELECT [显示列] FROM [表名] WHERE [判断参考列] IN ('条件值1','条件值2');
- 如果[条件值1]或[条件值2]在[判断参考列]中则筛选出来,和or类似,他们的查询性能一样。
# GROUP BY子句与聚合函数
# 介绍
GROUP BY子句会以指定列为参考将数据分成多个组,然后我们就能对各个组分别进行聚合函数操作。
GROUP BY后,如果是一个组对多个记录时,要显示这些记录就必须使用聚合函数才能显示。
比如: name money a 10 a 12 以name分组后就是: a 10 12 所以就需要使用聚合函数才能显示a的money
1
2
3
4
5
6
7
8
9
10
# 使用方法
SELECT [显示列],聚合函数(指定列) FROM [表名] GROUP BY [分组的列];
- 在单个列无法进行准确分组时,我们还能指定多个分组条件,来将分组条件变为(列1+列2),例如:GROUP BY [分组列1],[分组列2],...
# 常用聚合函数
聚合函数(指定列)
- 实际上我们也可以在没有组的情况下使用,会对查询的所有列进行操作MAX()
- 找出组中指定列的记录数值最大的值。MIN()
- 找出组中指定列的记录数值最小的值。COUNT()
- 计算出组中指定列的记录总个数。count(distinct 指定列)
- 去重(如果有相同的记录则只算作一次)。
GROUP_CONCAT()
- 将组中的指定列,其中的所有记录以一条数据行记录的形式打印。CONCAT()
- 可以将各列加上字符串拼接,作为一行记录输出,可以自己添加各种字符串。例如:
# 简单拼接 SELECT CONCAT("I'am ", username, ", hhh!") FROM user; # 生成分库分表备份命令语句 SELECT concat('mysqldump -uroot -p123',' ',table_schema,' ',table_name,' > ',table_name,'.sql') FROM information_schema.tables;
1
2
3
4
5
SUM()
- 计算出组中指定列的记录数值的总和AVG()
- 找出组中指定列的记录数值的总平均值
# HAVING子句
- 与WHERE功能一样,都是用于筛选条件,区别在于HAVING用于在GROUP BY后面进行判断,而因为在GROUP BY后面,所以可以利用聚合函数进行判断。
- 需要注意的是HAVING是不走索引的,所以如果前面得出结果数据量比较大的话,会对性能造成影响。
# ORDER BY子句
ORDER BY子句用于将查询结果按照指定列的记录值进行排序,默认是倒序排序(从小到大)。
使用方法:
# 按照指定列进行排序 SELECT [显示列] FROM [表名] ORDER BY [指定列]; # 按照聚合函数排序 SELECT [显示列],聚合函数(指定列) FROM [表名] GROUP BY [分组的列] ORDER BY [聚合函数](指定列);
1
2
3
4
5
6
7
8如果需要正叙排序需要在最后添加desc,例如:
order by [指定列] desc;
。
# LIMIT子句
LIMIT子句用于限制展示的行,跳过多少行,显示多少行。
使用方法:
# 仅显示前n行,跳过显示其他行 SELECT [显示列] FROM [表名] LIMIT n; # 仅显示指定范围的行,跳过m行,仅显示n行 SELECT [显示列] FROM [表名] LIMIT m,n;
1
2
3
4
5
6
# SELECT计算
- SELECT语句还可以用于计算,但如果在查询中使用计算,会大幅度降低查询效率,所以一般不会用。
- 例如:
SELECT 4*3+1;
# 集合查询语句
# UNION ALL
该语句的作用是将多个SELECT的查询结果合并。
该语句还可以用作SQL语句的优化,用来代替"WHERE OR",性能与其相比会好很多因为索引等级更高。
# 例如: SELECT email,username FROM user WHERE username="张三" UNION ALL SELECT email,username FROM user WHERE email="lisi@qq.com"; # 等同于OR↓ select email,username from user where username="张三" OR email="lisi@qq.com";
1
2
3
4
5
6
7
# UNION
- 与UNION ALL基本一样,区别在于UNION会做去重复,而UNION ALL不会做去重复,大多数情况UNION ALL使用的比较多。
# 多表连接查询(内连接)
# 介绍
- 在单表数据不能满足查询需求时,可进行跨多个表进行查询,它是通过匹配表A和表B相同含义的字段来实现多表连接。
- 多表连接会拿着表A的值去和表B的值进行匹配,虽然表A和表B放在FROM或JOIN的前后,得到的结果都一样。但是从性能角度考虑,在多表连接中,一般会选择数据行较少的表作为驱动表,也就是放在FROM后面的表,也就是最左侧的表。
- 另外子表的关联列,尽量是主键或唯一键,如果不能保证则至少应该建一个索引,来提高连表查询效率。
- 最核心的是要找到多张表之间的关联条件列,就是可以使不同表之间相互关联的列,只要表与表之间建立好直接或间接关系后,就可以向平常查询一样查了。
- 例如:表1有一个学生学号列,表2也有一个学生学号列,它们两个意义相同,他们就是关联条件列。
# 语法要求
- 连表查询中SELECT展示列的书写格式为
[表名].[列]
,以表示要展示的是哪个表的哪个列。 - 关联条件的书写格式为:
[表名].[列名] = [表名].[列名]
,需要注意关联条件必须是不同表的。
- 连表查询中SELECT展示列的书写格式为
# 连单张表
格式:
select [表名].[列名],... from [表1] join [表2] on [关联条件];
1
2例子:
SELECT user.email,info.name FROM user JOIN info ON user.id=info.user_id;
1
2
# 连多张表
多表之间的关联条件可以互相传递,比如表A和表C没有直接关系,但是有间接关系,而这个间接关系就是通过中间表B的字段来进行连接的。
格式:
SELECT [表名].[列名],...FROM [表1] JOIN [表2] ON [关联条件] JOIN [表3] ON [关联条件];
1
2
3例子:
SELECT FROM user.email,info.name,wallet.money FROM user JOIN info ON user.id=info.user_id JOIN wallet on info.wallet_id=wallet.id;
1
2
3
# JOIN/LEFT JOIN/RIGHT JOIN区别
# 别名应用
# 表别名
查询时,给表起别名,给表定别名后,子句调用表名时可以使用别名。在多表查询时一般会使用。
使用方法:
SELECT [别名1].[列],[别名2].[列] FROM [表名1] AS [别名1] JOIN [表名2] AS [别名2] ON [别名1].[列] = [别名2].[列] ...
1
2
3例如:
SELECT u.id, i.info FROM user as u JOIN info as i ON u.id=i.user_id ...
1
2
3
# 列别名
列别名在查询结果时,列名会变成列别名。列别名只能被GROUP BY之后的子句调用,因为SELECT是在GROUP BY后调用的。
例如:
SELECT username AS '用户名' FROM user ... HAVING username = 'lisi' ORDER BY `用户名`;
1
2
3
4
# CASE WHEN语句
CASE WHEN语句同等于IF判断。
聚合函数中用法:
函数(CASE WHEN 判断条件 then 成立命令 END) as 别名 函数(CASE WHEN 判断条件 then 成立命令 ELSE 不成立命令 END) as 别名 例如: select sum(CASE WHEN num = '01' then "123123" END) as T from test;
1
2
3
4
5SELECT中的用法:
SELECT Uid, CASE WHEN ly = 'app' then '手机' WHEN ly = 'pc' then '电脑' ELSE '其他' END AS '来源' FROM stu;
1
2
3
4
5
6
7