因为开放源代码这个数据库是免费的,由瑞典MySQL AB 公司开发,他现在是甲骨文公司的产品。
MySQL 最流行的关系型数据库管理系统,在 WEB 应用方面MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。
例外: 特外注意!!!!!!!!!!!!!
shell> mysql -ptest
shell> mysql -p test
第一个命令让mysql使用密码test,但没有指定默认数据库。第二个命令让mysql提示输入 密码并使用test作为默认数据库。
2. 关于指定数据库
可以在连接的时候就指定:mysql -uroot -p123 hardy2_db
mysqladmin -uroot -p password "123" (如果原密码没有,可以写成)
==
mysqladmin -uroot password "123"
mysqladmin -uroot -p 123 password "456"
[mysqld]
skip-grant-tables
[mysqld]
port=3306
character-set-server=utf8
collation-server=utf8_general_ci
default-storage-engine=innodb
innodb_file_per_table=1
basedir=C:\Users\pro3\Documents\mysql-5.7.25-winx64
datadir=C:\Users\pro3\Documents\mysql-5.7.25-winx64\data
[client]
port=3306
default-character-set=utf8
user=root
password=123
[mysql]
;port=3306
default-character-set=utf8
user=woai
password=456
作为研发,有一条铁律需要记住,那就是
!!!!永远不要相信用户的数据,哪怕他一再承诺是安全的
防止 SQL 注入要诀:
1. 永远不要信任用户的输入
2. 对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和双 "-" 进行转换等
3. 永远不要使用动态拼装 SQL ,可以使用 SQL 预处理语句
4. 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接
5. 不要把机密信息直接存放,加密或者 hash 掉密码和敏感的信息
6. 应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装
7. SQL 注入的检测方法一般采取辅助软件或网站平台来检测
grant select on *.* to 'egon1'@'localhost' identified by '123'; #只在user表中可以查到egon1用户的select权限被设置为Y
(授权 + 创建用户)
# 针对某一数据库:db1.*
grant select on db1.* to 'egon2'@'%' identified by '123'; #只在db表中可以查到egon2用户的select权限被设置为Y
# 针对某一个表:db1.t1
grant select on db1.t1 to 'egon3'@'%' identified by '123'; #只在tables_priv表中可以查到egon3用户的select权限
# 针对某一个字段:
grant select (id,name),update (age) on db1.t3 to 'egon4'@'localhost' identified by '123';
# 更改密码或者授权完成后,记得...:
# flush privileges; (这样就无需重启服务)
# 移除权限:
# revoke all privileges on hardy_db.tb1 from 'hardy9sap'@'%';
##############################################################################
#################################数据库操作#########################################
# 前言:
# 系统数据库:
# 1. information_schema:
虚拟库,不占用磁盘空间,用于存储数据库启动后的一些参数
# 2. performance_schema:
# 收集数据库服务器性能参数,记录处理查询请求时发生的各种事件、锁等现象
# 3. mysql:
# 授权库,存储系统用户的权限信息
# 4. test:
# MySQL数据库管理系统自动创建的用于测试的数据库
# 数据库命名规则:
1. 可以由字母、数字、下划线、$ (在设置变量的时候只有@可以用,而且要放在开头)
2. 不区分大小写
3. 唯一性
4. 不能使用关键字如 create select
5. 不能单独使用数字
6. 最长128位
# 创建数据库:
# ==================
`create database `根据给定的名称创建数据库,你需要拥有数据库的CREATE权限来使用这个语句。
`create schema`是`create database`的一个同义词。(也就是,都可以使用)
# =================
# CREATE DATABASE dbname [IF NOT EXISTS] [default character set] [collate];
# create database if not exists hardy_db [default] character set utf8 collate utf8_general_ci;
# create database hardy_db charset gbk collate gbk_chinese_ci;
# 删除数据库:
# drop database db1;
# 更改数据库的字符集:
# alter database db1 default charset=utf8 collate=utf8_general_ci;
# default 可以不写, charset / character set=utf8
# v8中, 使用utf8mb4字符集, 原因是早期的mysql只支持utf8的三个字节
# charset(字段), 可以查看字段的字符集
# 查看数据库创建信息:
# show create database db1;
# 查询所有的数据库:
# show databases;
# 访问某个数据库:
# use DB_NAME;
# 查询当前使用的数据库
# select database();
PS: 命令不会用,help create / help create database
###################################################################################################
# 前言:
# 什么是存储引擎?
# 表的类型,存储和操作此表的类型,类型的不同,对应着mysql不同的存取机制
# 存储引擎种类:
# 1. InnoDB,默认的,支持事务,原子性操作,支持回滚
# 2. MyISAM,不支持事务,全局索引,存储速度快
# 3. BlackHole,往表内插入任何数据,都相当于丢入黑洞,表内永远不存记录
# 5. Memory,数据存放在内存中,在重启mysql或者重启机器后,表内数据清空
# 5. mrg_myisam
# 6. performance_schema
# 7. federated 不支持
# 查看所有的存储引擎
# show engines;
# 查看当前使用的存储引擎
# show variables like 'storage_engine%';
# 指定存储引擎
# 1. 建表时指定
# create table tb1(id int, name char) engine=innodb;
# 2. 配置文件
# [mysqld]
# default-storage-engine=innodb
# innodb_file_per_table=1
# PS: .ibd innodb表数据 .frm表结构 blackhole / memory只有表结构 (也说明了一个表由多个文件组成)
####################################################################################################
# 创建表:
# 语法:
create [temporary] table 表名(
字段名1 类型[(宽度) 约束条件],
字段名2 类型[(宽度) 约束条件],
字段名3 类型[(宽度) 约束条件]
);
#注意:
1. 在同一张表中,字段名是不能相同
2. 宽度和约束条件可选
3. 字段名和类型是必须的
4. 注意注意注意:表中的最后一个字段不要加逗号
# 创建表:
# create table [if not exists] t1(id int, name char) engine=innodb [default] charset=utf8mb4 collate=utf8mb4_0900_ai_ci;
# default可以不写
# create table t2(id int not null auto_increment primary key) engine=innodb character set=utf8 collate=utf8_general_ci;
# 表的字段默认可以为空,也就是为空值Null(default null)
# not null:
表示该字段不能为空值Null;
# auto_increment:
表示自增,用这个的时候一般配合 primary key 使用,
# primary key:
它表示主键,它的作用:第一,约束该字段不能重复并且不能为空;
第二,增加查找速度
# 一个表只能有一个自增并且只能有一个主键
# 删除表:
# drop table [if exists] t1;
# 重命名表:
# alter table t11 rename as/to t12; as/to不是必须的
# rename table t11 to t12; to不可省略
# 更改存储引擎
# alter table tb3 engine=innodb;
# 更改表的字符集
# alter table tb3 [default] charset=gbk collate=gbk_chinese_ci;
# 查看表的字段信息:
# describe t11;
# desc t11;
# show columns from t11; 三者等价
# 另外, show columns from t11; 这种形式, 是可以接like来过滤的.
# 查看表的详细索引信息
# show index from t11 \G
# 查看指定数据库下所有表的信息
# show table status from hardy2_db \G
# show table status from hardy2_db like '%tb%' \G
# 查看表的创建信息:
# show create table t11 \G \G 竖向显示
# 查询所有的表:
# show tables;
# 从一个表复制到另一个表(复制表结构 + 表记录, 但表结构中的索引会丢失)
# create table student_copy as select * from student;
# == create table student_copy select * from student;
# 只复制表结构
create table student_copy select * from student where 1=2; # 条件为假,查不到任何记录,但是有结构
# 只复制表结构2(只会完整复制原表的建表语句)
create table new_stu like student;
# 更改表字段(名字、类型、约束性条件):[column]可选 [完整性约束条件…]可选,但必须要指定类型
# alter table tb1 change [column] name NAME char(5) first; first, 将字段插入到最前面
# alter table tb1 change NAME name char after ident; after, 将字段插入到指定字段的后面
# 注意: 虽然将小写的name更改为了大写的NAME, 而且在表中也显示了大写的NAME, 但是仍然不区分大小写.
# (相当于以下两步 == 以上一步)
# 重命名表字段名字:
# alter table tb1 rename name to NAME;
# 修改表字段的定义(类型、约束性条件):
# alter table tb1 modify [column] name char(5) first;
# alter table tb1 modify name char after ident;
# 删除字段:[column]可选
# alter table userinfo drop [column] tel;
# 注意: 如果只有一个字段,不能使用这个语法,只能删除整张表.
# 因为有字段的才称之为表,连一个字段都没了,那么表也就没有存在的意义了。
# 插入字段:[column]可选 [完整性约束条件…]可选,但必须要指定类型
# alter table userinfo add [column] tel char not null;
# alter table userinf add gender char, add name char(5);
# alter table userinfo add score int default 100 FIRST; first, 将字段插入到最前面
# alter table userinfo add address char(10) AFTER name; after, 将字段插入到指定字段的后面
# =================================================================================
# 如果主键使用单列,那么这列必须不能为空而且不能重复;
# 如果主键为多列,那么列的组合必须唯一;
# create table t1(id int auto_increment, index(id));
# alter table t1 add primary key(id, name);
# alter table t1 modify id int primary key;
# alter table t1 drop primary key;
#################################################################################################
# 语法:
# 插入记录:[into]可以省略; values or value
# insert [into] t1(id, name) values/value(1, 'alex'); # 一次插入一行记录
# insert t1(id, name) values(2, 'egon'), (3, 'peiqi'); # 一次插入多行记录
# 如果id是auto_increment, 可以不用指定值
# insert t1(id, name) values(2, '');
UPDATE [LOW_PRIORITY] [IGNORE] table_reference
SET assignment_list
[WHERE where_condition]
[ORDER BY ...]
[LIMIT row_count]
UPDATE [LOW_PRIORITY] [IGNORE] table_references
SET assignment_list
[WHERE where_condition]
在 MySQL/MariaDB 中,有三种主要的类型:
Text (文本)
Number (数字)
Date/Time (日期/时间) 类型
Text 类型
数据类型 描述
CHAR(size) 保存固定长度的字符串(可包含字母、数字以及特殊字符)
在括号中指定字符串的长度
最多 255 个字符
VARCHAR(size) 保存可变长度的字符串(可包含字母、数字以及特殊字符)
在括号中指定字符串的最大长度
最多 255 个字符
注意:如果值的长度大于 255,会被转换为 TEXT 类型
TINYTEXT 存放最大长度为 255 个字符的字符串
TEXT 存放最大长度为 65,535 个字符的字符串
BLOB 用于 BLOBs(Binary Large OBjects)
存放最多 65,535 字节的数据。
MEDIUMTEXT 存放最大长度为 16,777,215 个字符的字符串
MEDIUMBLOB 用于 BLOBs(Binary Large OBjects)
存放最多 16,777,215 字节的数据
LONGTEXT 存放最大长度为 4,294,967,295 个字符的字符串
LONGBLOB 用于 BLOBs (Binary Large OBjects)
存放最多 4,294,967,295 字节的数据。
ENUM(x,y,z,...) 允许输入可能值的列表
可以在 ENUM 列表中列出最大 65535 个值
如果列表中不存在插入的值,则插入空值
注意:存储的值是按照输入的顺序排序的
可以按照此格式输入可能的值 ENUM('X','Y','Z')
SET 与 ENUM 类似,但 SET 最多只能包含 64 个列表项且 SET 可存储一个以上的选择
Number 类型
数据类型 描述
TINYINT(size) 带符号 -128 到 127 ,无符号 0 到 255
SMALLINT(size) 带符号范围 -32768 到 32767
无符号 0 到 65535
size 默认为 6
MEDIUMINT(size) 带符号范围 -8388608 到 8388607
无符号的范围是 0 到 16777215
size 默认为 9
INT(size) 带符号范围 -2147483648 到 2147483647
无符号的范围是 0 到 4294967295
size 默认为 11
BIGINT(size) 带符号的范围是 -9223372036854775808 到 9223372036854775807
无符号的范围是0到 18446744073709551615
size 默认为 20
FLOAT(size,d) 带有浮动小数点的小数字
在 size 参数中规定显示最大位数
在 d 参数中规定小数最大位数
DOUBLE(size,d) 带有浮动小数点的大数字
在 size 参数中规显示定最大位数
在 d 参数中规定小数的最大位数
DECIMAL(size,d) 作为字符串存储的 DOUBLE 类型,允许固定的小数点
在 size 参数中规定显示最大位数
在 d 参数中规定小数点右侧的最大位数
上表中的 size 并不是指存储在数据库中的具体的长度,而是显示的位数。如 int(4) 并不是只能存储4个长度的数字
int(size) 所占多少存储空间并无任何关系。
int(3)、int(4)、int(8) 在磁盘上都是占用 4 btyes 的存储空间。
就是在显示给用户的方式有点不同外,int(M) 跟 int 数据类型是相同的
例如:int 的 值为 10 (指定zerofill)
int(9)显示结果为000000010
int(3)显示结果为010
就是显示的长度不一样而已 都是占用四个字节的空间
Date 类型
数据类型 描述
DATE() 日期。格式:YYYY-MM-DD
支持的范围是从 '1000-01-01' 到 '9999-12-31'
DATETIME() *日期和时间的组合
格式:YYYY-MM-DD HH:MM:SS
支持的范围是从 '1000-01-01 00:00:00' 到 '9999-12-31 23:59:59'
TIMESTAMP() *时间戳
TIMESTAMP 值使用 Unix 纪元('1970-01-01 00:00:00' UTC) 至今的秒数来存储
格式:YYYY-MM-DD HH:MM:SS
支持的范围是从 '1970-01-01 00:00:01' UTC 到 '2038-01-09 03:14:07' UTC
TIME() 时间
格式:HH:MM:SS
支持的范围是从 '-838:59:59' 到 '838:59:59'
YEAR() 2 位或 4 位格式的年
4 位格式所允许的值:1901 到 2155
2 位格式所允许的值:70 到 69 表示从 1970 到 2069
* 虽然 DATETIME 和 TIMESTAMP 返回相同的格式,它们的工作方式是不同的
在 INSERT 或 UPDATE 查询中,TIMESTAMP 自动把自身设置为当前的日期和时间。
TIMESTAMP 也接受不同的格式,比如 YYYYMMDDHHMMSS、YYMMDDHHMMSS、YYYYMMDD 或 YYMMDD
SQL 中, NULL 与 空字符串, 零 都不相同。是指为未定义或是未知的值
NULL 值的处理方式与其它值不同: 无法比较 NULL 和 0;它们是不等价的
mysql> SET @v1 = b'000010101' | b'000101010';
mysql> SET @v2 = _binary b'000010101' | _binary b'000101010';
mysql> select @v1, @v2;
+
| @v1 | @v2 |
+
| 63 | ? |
+
+
| x |
+
| -128 |
| -128 |
| 127 |
| 127 |
+
smallint 2个字节
mediumint 3个字节
在表达式或 UNION 查询中涉及列时,会自动忽略 ZEROFILL 属性。
使用 ZEROFILL 属性时要注意,如果将大于显示宽度的值存储在具有 ZEROFILL 属性的整数列中,
为某些复杂连接生成临时表时可能会遇到问题。
因为在这些情况下,MySQL 会假定数据值符合列显示宽度
PS:注意:为该整类型指定宽度时,仅仅只是指定查询结果的显示宽度,与存储范围无关,超过宽度了,原样输出
其实我们完全没必要为整数类型指定显示宽度,使用默认的就可以了
默认的显示宽度,都是在最大值的基础上加1
有符号和无符号的最大数字需要的显示宽度均为10,
而针对有符号的最小值则需要11位才能显示完全,所以int类型默认的显示宽度为11是非常合理的
最后:整形类型,其实没有必要指定显示宽度,使用默认的就ok
作用:存储薪资、身高、体重、体质参数等
如果有d,但位数不足的话,补0,反正要d位,d最大30
单精度浮点数(非准确小数值),数值越大越不准,4个字节
m是数字总个数,d是小数点后个数。m最大值为255,d最大值为30
双精度浮点数(非准确小数值),精度比float要高,数值越大也会变得不准确,8个字节
m是数字总个数,d是小数点后个数。m最大值为255,d最大值为30
存储:
存储char类型的值时,会往右填充空格来满足长度
例如:指定长度为10,存>10个字符则报错,存<10个字符则用空格填充直到凑够10个字符存储
检索:
在检索或者说查询时,查出的结果会自动删除尾部的空格,除非我们打开pad_char_to_full_length SQL模式(SET sql_mode = 'PAD_CHAR_TO_FULL_LENGTH';)
注:对于空间问题,假设char和varchar的字符数都是4,那么当要存储的字符数<4时,
varchar确实会节省空间;但是当要存储的字符数=4时,varchar反而会浪费空间。
字符长度范围:0-65535(如果大于21845会提示用其他类型 。
mysql行最大限制为65535字节,字符编码为utf-8:https:
存储:
varchar类型存储数据的真实内容,不会用空格填充,如果'ab ',尾部的空格也会被存起来
强调:varchar类型会在真实数据前加1-2Bytes的前缀,该前缀用来表示真实数据的bytes字节数(1-2Bytes最大表示65535个数字,正好符合mysql对row的最大字节限制,即已经足够使用)
如果真实的数据<255bytes则需要1Bytes的前缀(1Bytes=8bit 2**8最大表示的数字为255)
如果真实的数据>255bytes则需要2Bytes的前缀(2Bytes=16bit 2**16最大表示的数字为65535)
检索:
尾部有空格会保存下来,在检索或者说查询时,也会正常显示包含空格在内的内容
1. char填充空格来满足固定长度,但是在查询时却会很不要脸地删除尾部的空格(装作自己好像没有浪费过空间一样),
然后修改sql_mode让其现出原形
set sql_mode = 'PAD_CHAR_TO_FULL_LENGTH';
set sql_mode = ''; 还原
2. 虽然 CHAR 和 VARCHAR 的存储方式不太相同,但是对于两个字符串的比较,都只比 较其值,忽略 CHAR 值存在的右填充,
即使将 SQL _MODE 设置为 PAD_CHAR_TO_FULL_ LENGTH 也一样,,但这不适用于like
注:虽然varchar使用起来较为灵活,但是从整个系统的性能角度来说,char数据类型的处理速度更快,有时甚至可以超出varchar处理速度的50%。
因此,用户在设计数据库时应当综合考虑各方面的因素,以求达到最佳的平衡
SQL优化建议:
1. 将定长的数据放在前面,不定长的数据放在后面
3. 最好不要在一张表中混用char和varchar
2. >255个字符,超了就把文件路径存放到数据库中。
比如图片,视频等找一个文件服务器,数据库中只存路径或url。
效率:char>varchar>text
结论:
1、经常变化的字段用varchar;
2、知道固定长度的用char;
3、超过255字节的只能用varchar或者text;
4、能用varchar的地方不用text;
5、能够用数字类型的字段尽量选择数字类型而不用字符串类型,这会降低查询和连接的性能,并会增加存储开销。
这是因为引擎在处理查询和连接会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了;
6、同一张表出现多个大字段,能合并时尽量合并,不能合并时考虑分表,原因请考 优化InnoDB表BLOB,TEXT列的存储效率https:
单独插入时间时,需要以字符串的形式,按照对应的格式(只能是:分隔)插入
============注意啦,注意啦,注意啦===========
1. 单独插入时间时,需要以字符串的形式,按照对应的格式插入
2. 插入年份时,尽量使用4位值
3. 插入两位年份时,<=69,以20开头,比如50, 结果2050
>=70,以19开头,比如71,结果1971
datetime与timestamp的区别
在实际应用的很多场景中,MySQL的这两种日期类型都能够满足我们的需要,存储精度都为秒,但在某些情况下,会展现出他们各自的优劣。
下面就来总结一下两种日期类型的区别。
1. DATETIME的日期范围是1001——9999年,TIMESTAMP的时间范围是1970——2038年。
2. timestamp 会将值的时区从当前时区转换为 UTC 然后存储,并在需要显示和检索时从 UTC 转换回当前时区
但对于 datetime 类型,什么都不会发生,值是什么时区,存储的就是什么时区
默认情况下,timestamp 和 datetime 都会将当前连接的 MySQL 服务器的时区当作当前时区,当然了,这个时区是可配置的,而且可以针对每个连接单独配置。
从某些方面说,在数据转移或者在不同地方显示时,只要设置了一样的时区,那么数据就是一致的,否额
datetime 的值虽然存储和显示的时候都是同一个值,但可能不是我们想要的,因为时区错了
timestamp 虽然可以保证时间是正常的,但存储的值和显示的值可能会不一样,可能会导致我们错觉发生
3. DATETIME使用8字节的存储空间,TIMESTAMP的存储空间为4字节。因此,TIMESTAMP比DATETIME的空间利用率更高。
4. MySQL 还为 datetime 和 timestamp 两种日期时间类型提供了自动赋值功能。
也就是在 MySQL INSERT 时可以自动初始化为当前日期时间,在 MySQL Update 时自动更新为当前时间。
CREATE TABLE t1 (
ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
dt DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
====================不要怀疑, 就是update, 没有insert=============================================
两个约束
DEFAULT CURRENT_TIMESTAMP 该列会自动设置当前时间为默认值
ON UPDATE CURRENT_TIMESTAMP 自动更新为当前时间戳
1. DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 指示在 insert 操作时自动插入当前日期时间
2. DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 指示在 update 操作时自动更新为当前日期时间
对于timestamp, insert的时候可以这样values() / values(null) / 自已指定 / values(default); 在update的时候, 自动更新
对于datetime, insert的时候可以这样values() / 自已指定 / values(default) / 不能values(null); 在update的时候, 自动更新
=====================================================================================================
2.使用了 default 约束,当没有使用 ON UPDATE CURRENT_TIMESTAMP 约束时,
该列会自动使用给定的默认值,但不会自动更新为当前时间戳
而默认的值取决于 default 子句是指定 CURRENT_TIMESTAMP 还是常量值。
如果使用 CURRENT_TIMESTAMP,默认值是当前时间戳
CREATE TABLE t1 (
ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
dt DATETIME DEFAULT CURRENT_TIMESTAMP
);
=====================================================================================================
5. 对于这三种日期时间类型,无效的值将会转换为相应的 零 值,
也就是,date 类型会转换为 0000-00-00 ,
datetime 类型会转换为 0000-00-00 00:00:00 ,
而 timestamp 则会转换为 1970-01-01 00:00:00
6. 日期时间类型,还可以各取所长
例子: create table employee(d date, t time, dt datetime);
insert employee values(now(), now(), now());
单选 只能在给定的范围内选一个值
1. 创建表结构时指定的枚举值都会分配一个内部索引,索引的下标从 1 开始
2. 注意:下标并不是从 0 开始,而 0 则具有其它的意义
空字符串错误值的索引为 0,这样,我们可以直接使用 0 值来查询那些插入的或更新的无效的枚举值
(新版中, 插入不存在, 直接报错)
3. NULL 值的索引为 NULL
枚举值 索引
NULL NULL
'' 空字符串 0
'Mercury' 1
'Venus' 2
'Earth' 3
4. ENUM 最多只能包含 65,535 个不同的枚举值
5. 如果在数字上下文中检索 ENUM 值,则返回列值的索引
SELECT enum_col+0 FROM tbl_name;
6. 当在 ENUM 列上使用 SUM() 或 AVG() 等聚合函数时,
因为这些函数的参数必须是一个数字,所以 MySQL 会自动使用它们的索引值作为参数。
也就是说,对于需要计算的场景,都会使用内部索引。
其实,真实的枚举值,只有在插入或者显示或者查询时才会用到。
7. ENUM的字面量处理
在创建表结构时,MySQL 会自动删除 ENUM 枚举值的尾随空格,例如会把 'medium ' 转换成 'medium'。
检索时,MySQL 会自动将存储的内部索引转换为定义时指定的相应的 enum 枚举值字面量。
8. 不建议使用看起来像数字的枚举值来定义 ENUM 列,
因为这很容易让人感到困惑,分不清传递(引用) 的到底是枚举值字面量还是内部索引。
例如,以下列的枚举成员的字符串值为 '0'、'1' 和 '2',而数字索引值为 1 、2 和 3
numbers ENUM('0','1','2')
如果我们在插入数据或者更新数据时指定存储 2 ,因为会被解释为索引值,所以实际存储的枚举值为 '1' ( 索引为 2 的值 )。
而如果我们存储 '2' ,因为枚举值字面量 '2' 存在,所以存储的值也为 2 。
但如果我们存储 '3' ,因为枚举值字面量 '3' 并不存在,那么它就会被视为是内部索引 3 ,进而存储的实际值其实是 '2'
mysql> INSERT INTO t (numbers) VALUES(2),('2'),('3');
mysql> SELECT * FROM t;
+
| numbers |
+
| 1 |
| 2 |
| 2 |
+
如果要确定 ENUM 列的所有可能值,
SHOW COLUMNS FROM tbl_name LIKE 'enum_col' 语句可以解析出 enum_col 列中的所有 enum 定义
9. 如果一个 ENUM 列添加了 NULL 约束,那么这个 ENUM 列就允许 NULL 值,且默认的值就是 NULL
如果一个 ENUM 列添加了 NOT NULL 约束,那么它的默认值就是第一个枚举值。
10. ENUM 枚举值的排序问题
因为 ENUM 类型存储的是枚举值的内部索引,
所以 ENUM 值根据其索引号进行排序,具体显示出来,则取决于定义列是的枚举成员顺序。
https:
例如,如果在定义列时,指定了 'b' 在 'a' 前面 ('b','a'),那么 'b' 的顺序就会在 'a' 之前,且空字符串在非空字符串之前排序,NULL 值在所有其他枚举值之前排序
也就是排序的顺序默认是 NULL '' 'b' 'a'
这是一个大坑啊,为了避免这个坑,为了在 ENUM 列上使用 ORDER BY 子句时防止出现意外结果,则需要做如下选择
指定 ENUM 列的排序顺序使用字母顺序表
或者使用 ORDER BY CAST (col AS CHAR) 或 ORDER BY CONCAT(col) 确保 enum 列按词法排序而不是索引编号排序
11. ENUM 数据类型的一些限制
1. 枚举值不能是表达式,即使该表达式用于计算字符串值。
例如,下面的建表语句是无效的,会执行失败,因为 CONCAT()函数不能用于构造枚举值
CREATE TABLE sizes (
size ENUM('small', CONCAT('med','ium'), 'large')
);
2. 不能使用用户变量作为枚举值。例如下面的语句也是无效的
SET @mysize = 'medium';
CREATE TABLE sizes (
size ENUM('small', @mysize, 'large')
);
3. 我们强烈建议不要使用数字用作枚举值,
因为它不会通过适当的 TINYINT 或 SMALLINT 类型保存在存储上。
而且,如果你错误地引用 ENUM 值,很容易混淆枚举字面量和底层索引值 ( 可能不相同 )
4. ENUM 列定义中的重复值会导致警告,如果启用了严格的 SQL 模式,则会出错
多选 在给定的范围内可以选择一个或一个以上的值
MySQL支持JSON数据类型。相比于Json格式的字符串类型,JSON数据类型的优势有:
存储在JSON列中的JSON文档的会被自动验证。无效的文档会产生错误;
最佳存储格式。存储在JSON列中的JSON文档会被转换为允许快速读取文档元素的内部格式。
存储在JSON列中的任何JSON文档的大小都受系统变量max_allowed_packet的值的限制,
可以使用JSON_STORAGE_SIZE()函数获得存储JSON文档所需的空间。
JSON值的局部更新
在MySQL8.0中,优化器可以执行JSON列的局部就地更新,而不用删除旧文档再将整个新文档写入该列。局部更新的条件:
1. 正在更新的列被声明为JSON;
2. 该UPDATE语句使用任一的三个函数 JSON_SET(), JSON_REPLACE()或 JSON_REMOVE()更新列;
3. 输入列和目标列必须是同一列;
4. 所有更改都使用新值替换现有数组或对象值,并且不向父对象或数组添加任何新元素;
5. 新值不能大于旧值;
创建JSON值:
JSON数字
'10'
JSON字符串
'"HELLO"'
JSON数组
["abc", 10, null, true, false]
JSON对象
{"k1": "value", "k2": 10}
嵌套
[99, {"id": "HK500", "cost": 75.99}, ["hot", "cold"]]
{"k1": "value", "k2": [10, 20]}
例子:
mysql> CREATE TABLE t_json (jdoc JSON) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected, 1 warning (0.73 sec)
mysql> INSERT INTO t_json VALUES('[1,2]');
Query OK, 1 row affected (0.17 sec
mysql> INSERT INTO t_json VALUES('{"key1":"value1","key2":"value2"}');
Query OK, 1 row affected (0.27 sec)
mysql> INSERT INTO t_json VALUES('"HELLO"');
Query OK, 1 row affected (0.20 sec)
JSON_TYPE()函数:
JSON_TYPE()函数尝试将传入的值其解析为JSON值。如果值有效,则返回值的JSON类型,否则产生错误:
例如:
SELECT JSON_TYPE('["a","b",true,13]');
JSON_ARRAY()函数:
JSON_ARRAY()接收传入的值列表(可以为空),返回包含这些值的JSON数组;
例如:
SELECT JSON_ARRAY("ab",false,13);
SELECT JSON_ARRAY();
JSON_OBJECT()函数:
JSON_OBJECT() 接收传入的键值对列表(可以为空),并返回包含这些键值对的JSON对象:
例如:
select json_object("key1", "a", "key2", 20);
select json_object();
JSON_MERGE_PRESERVE()函数:
JSON_MERGE_PRESERVE() 获取两个或多个JSON文档并返回组合结果:
例如:
SELECT JSON_MERGE_PRESERVE('["a", 1]', '{"key": "value"}');
因此我们也可以使用以上三种方法向表中添加JSON值,可以一定程度地避免输入格式错误:
mysql> INSERT INTO t_json VALUES(JSON_ARRAY('json_array'));
Query OK, 1 row affected (0.19 sec)
mysql> INSERT INTO t_json VALUES(JSON_OBJECT('key','hello'));
Query OK, 1 row affected (0.09 sec)
mysql> INSERT INTO t_json VALUES(JSON_MERGE_PRESERVE(JSON_OBJECT('key','hello'),JSON_ARRAY(1,2)));
Query OK, 1 row affected (0.14 sec)
JSON值的规范化,合并和自动包装:
解析字符串并发现字符串是有效的JSON文档时,它在被解析时也会被规范化。
对于重复的键(key),后面的值(value)会覆盖前面的值。
例如:
SELECT JSON_OBJECT('x',1,'y',2,'x','a','x','b');
合并JSON值:
MySQL8.0.3及更高版本中,有两种合并函数:JSON_MERGE_PRESERVE()和 JSON_MERGE_PATCH()。
下面具讨论它们的区别。
合并数组:
SELECT JSON_MERGE_PATCH('[1, 2]', '["a", "b", "c"]','[1, 2]', '[true, false]');
SELECT JSON_MERGE_PRESERVE('[1, 2]', '["a", "b", "c"]','[1, 2]', '[true, false]');
合并数组时,
JSON_MERGE_PATCH只保留最后传入的数组参数,
而JSON_MERGE_PRESERVE则按传入顺序将数组参数连接。
两者必须有2个参数及以上
合并对象:
SELECT JSON_MERGE_PATCH('{"a": 3, "b": 2}', '{"c": 3, "a": 4}', '{"c": 5, "d": 3}');
SELECT JSON_MERGE_PRESERVE('{"a": 3, "b": 2}', '{"c": 3, "a": 4}', '{"c": 5, "d": 3}');
合并对象时:
合并对象时,对于重复键,JSON_MERGE_PRESERVE只保留最后传入的键值,
而JSON_MERGE_PRESERVE重复键的所有值保留为数组。
搜索和修改JSON值:
在了解搜索和修改JSON值之前,先来看看JSON的路径语法。
路径语法:
1. .keyName:JSON对象中键名为keyName的值;
2. 对于不合法的键名(如有空格),在路径引用中必须用双引号"将键名括起来,例,."key name";
3. [index]:JSON数组中索引为index的值,JSON数组的索引同样从0开始;
4. [index1 to index2]:JSON数组中从index1到index2的值的集合;
5. .*: JSON对象中的所有value;放入一个数组中
6. [*]: JSON数组中的所有值;
7. prefix**suffix: 以prefix开头并以suffix结尾的路径;
8. **.keyName为多个路径,如对于JSON对象'{"a": {"b": 1}, "c": {"b": 2}}','$**.b'指路径$.a.b和$.c.b;
9. 不存在的路径返回结果为NULL;
10. 前导$字符表示当前正在使用的JSON文档
例子:对于数组[3, {"a": [5, 6], "b": 10}, [99, 100]]
$[1]为{"a": [5, 6], "b": 10}。
[1].a为[5, 6]。
$[1].a[1]为 6。
$[1].b为 10。
$[2][0]为 99。
搜索:
JSON_EXTRACT提取JSON值
JSON对象
SELECT JSON_EXTRACT('{"id": 29, "name": "Taylor"}', '$.name');
-- "Taylor"
SELECT JSON_EXTRACT('{"id": 29, "name": "Taylor"}', '$.*');
-- [29, "Taylor"]
JSON数组
SELECT JSON_EXTRACT('["a", "b", "c"]', '$[1]');
-- "b"
SELECT JSON_EXTRACT('["a", "b", "c"]', '$[1 to 2]');
-- ["b", "c"]
SELECT JSON_EXTRACT('["a", "b", "c"]', '$[*]');
-- ["a", "b", "c"]
修改:
1. JSON_REPLACE 替换值(只替换已经存在的旧值)
2. JSON_SET 设置值(替换旧值,并插入不存在的新值)
3. JSON_INSERT 插入值(插入新值,但不替换已经存在的旧值)
4. JSON_REMOVE 删除JSON数据,删除指定值后的JSON文档
JSON_REPLACE与JSON_SET的区别:
// 旧值存在
mysql> SELECT JSON_REPLACE('{"id": 29, "name": "Taylor"}', '$.name', 'Mere');
+----------------------------------------------------------------+
| JSON_REPLACE('{"id": 29, "name": "Taylor"}', '$.name', 'Mere') |
+----------------------------------------------------------------+
| {"id": 29, "name": "Mere"} |
+----------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT JSON_SET('{"id": 29, "name": "Taylor"}', '$.name', "Mere");
+------------------------------------------------------------+
| JSON_SET('{"id": 29, "name": "Taylor"}', '$.name', 'Mere') |
+------------------------------------------------------------+
| {"id": 29, "name": "Mere"} |
+------------------------------------------------------------+
1 row in set (0.00 sec)
// 旧值不存在
mysql> SELECT JSON_REPLACE('{"id": 29, "name": "Taylor"}', '$.cat', 'Mere');
+---------------------------------------------------------------+
| JSON_REPLACE('{"id": 29, "name": "Taylor"}', '$.cat', 'Mere') |
+---------------------------------------------------------------+
| {"id": 29, "name": "Taylor"} |
+---------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT JSON_SET('{"id": 29, "name": "Taylor"}', '$.cat', 'Mere');
+-----------------------------------------------------------+
| JSON_SET('{"id": 29, "name": "Taylor"}', '$.cat', 'Mere') |
+-----------------------------------------------------------+
| {"id": 29, "cat": "Mere", "name": "Taylor"} |
+-----------------------------------------------------------+
1 row in set (0.00 sec)
JSON_INSERT和JSON_SET:
// 旧值存在
mysql> SELECT JSON_INSERT('[1, 2, 3]', '$[1]', 4);
+-------------------------------------+
| JSON_INSERT('[1, 2, 3]', '$[1]', 4) |
+-------------------------------------+
| [1, 2, 3] |
+-------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT JSON_SET('[1, 2, 3]', '$[1]', 4);
+----------------------------------+
| JSON_SET('[1, 2, 3]', '$[1]', 4) |
+----------------------------------+
| [1, 4, 3] |
+----------------------------------+
1 row in set (0.00 sec)
//旧值不存在
mysql> SELECT JSON_INSERT('[1, 2, 3]', '$[4]', 4);
+-------------------------------------+
| JSON_INSERT('[1, 2, 3]', '$[4]', 4) |
+-------------------------------------+
| [1, 2, 3, 4] |
+-------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT JSON_SET('[1, 2, 3]', '$[4]', 4);
+----------------------------------+
| JSON_SET('[1, 2, 3]', '$[4]', 4) |
+----------------------------------+
| [1, 2, 3, 4] |
+----------------------------------+
1 row in set (0.00 sec)
JSON_REMOVE:
mysql> SELECT JSON_REMOVE('[1, 2, 3]', '$[1]');
+----------------------------------+
| JSON_REMOVE('[1, 2, 3]', '$[1]') |
+----------------------------------+
| [1, 3] |
+----------------------------------+
1 row in set (0.00 sec)
mysql> SELECT JSON_REMOVE('[1, 2, 3]', '$[4]');
+----------------------------------+
| JSON_REMOVE('[1, 2, 3]', '$[4]') |
+----------------------------------+
| [1, 2, 3] |
+----------------------------------+
1 row in set (0.00 sec)
mysql> SELECT JSON_REMOVE('{"id": 29, "name": "Taylor"}', '$.name');
+-------------------------------------------------------+
| JSON_REMOVE('{"id": 29, "name": "Taylor"}', '$.name') |
+-------------------------------------------------------+
| {"id": 29} |
+-------------------------------------------------------+
1 row in set (0.00 sec)
# =========================================================================================================
#############################################################################################################
# 完整性约束:用于保证数据的完整性和一致性
# 为了防止不符合规范的数据进入数据库,在用户对数据进行插入、修改、删除等操作时,
# DBMS自动按照一定的约束条件对数据进行监测,
# 使不符合规范的数据不能进入数据库,以确保数据库中存储的数据正确、有效、相容。
# 约束可以在创建表时指定 ( 通过 CREATE TABLE 语句 ),或者在表创建之后设置 ( 通过 ALTER TABLE 语句 )
# =====================================================
SQL CREATE TABLE + CONSTRAINT 语法
CREATE TABLE table_name (
column_name1 data_type ( size ) constraint_name ,
column_name2 data_type ( size ) constraint_name ,
column_name3 data_type ( size ) constraint_name ,
....
);
SQL 规范中,可以使用下面这些约束
约束 说明
NOT NULL 指示某列不能存储 NULL 值
UNIQUE 保证某列的每行必须有唯一的值
PRIMARY KEY NOT NULL 和 UNIQUE 的结合。确保某列(或两个列多个列的结合)有唯一标识
有助于更容易更快速地找到表中的一个特定的记录
FOREIGN KEY 保证一个表中的数据匹配另一个表中的值的参照完整性
CHECK 保证列中的值符合指定的条件
DEFAULT 规定没有给列赋值时的默认值
# =====================================================
# 1. null、not null
null,1. 表示可以为空(默认不用传值,使用default的值,default默认值为null)
2. 表示可以为空值(也就是传null值,并非空字符串)
not null,1. 表示不能为空(也就是必须传值,当然也可以使用default的值,
但是default的值不能是null值)
2. 表示不能为空值(也就是不能传null值)
# =========================================================
CREATE TABLE 时的 SQL NOT NULL 约束:
在创建表结构时,可以给字段添加 NOT NULL 关键字来添加 NOT NULL 约束
CREATE TABLE lesson (
id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
name varchar(32) default '',
views int(11) NOT NULL default '0',
created_at DATETIME
);
ALTER TABLE 时的 SQL not null 约束:
如果表已经被创建,而又想添加 NOT NULL 约束,可以使用 ALTER TABLE 命令
ALTER TABLE lesson MODIFY COLUMN views int(11) NOT NULL default '0';
删除 NOT NULL 约束
如果想要删除 NOT NULL 约束,可以使用 ALTER TABLE 命令,
也就是不指定 NOT NULL 关键字即可。
ALTER TABLE lesson MODIFY COLUMN views int(11) default '0';
# ===========================================================
# 2. default
# ===============================================================
default,
默认值,创建列时可以指定默认值,缺省的默认值是NULL,
当插入数据时如果未主动设置,则自动添加默认值
注意:当设置为not null时,default不能为null
DEFAULT 约束中指定的默认值可以是文字常量或表达式。
如果使用表达式作为默认值,则需要表达式默认值括在括号内 () ,以将它们与文字常量默认值区分开来。
例如:
CREATE TABLE t1 (
-- 常量默认值
i INT DEFAULT 0,
c VARCHAR(10) DEFAULT '',
-- 表达式默认值
f FLOAT DEFAULT (RAND() * RAND()),
b BINARY(16) DEFAULT (UUID_TO_BIN(UUID())),
d DATE DEFAULT (CURRENT_DATE + INTERVAL 1 YEAR),
p POINT DEFAULT (Point(0,0)),
j JSON DEFAULT (JSON_ARRAY())
);
但是,这有一个例外。这个例外就是: TIMESTAMP 和 DATETIME 列。
对于 TIMESTAMP 和 DATETIME 列,我们可以将 CURRENT_TIMESTAMP 函数指定为默认值,而需要添加括号。
表达式默认值必须遵守以下规则。如果表达式包含不允许的构造,则会发生错误
1. 允许使用文字,内置函数(确定性和非确定性)和运算符
2. 不允许使用子查询,参数,变量,存储函数和用户定义的函数
3. 表达式默认值不能依赖于具有 AUTO_INCREMENT 属性的列。
4. 某一列的表达式默认值可以引用另外一张表中的列,但是对生成的列或具有表达式默认值的列的引用必须是对于在表定义中较早出现的列。
也就是说,表达式默认值不能包含对生成的列或具有表达式默认值的列的前向引用。翻译成白话文就是,引用的列必须已经存在。
5. 排序 ( ordering ) 约束也适用于使用 ALTER TABLE 重新排序表列。
如果结果表的表达式默认值包含对具有表达式默认值的生成列或列的前向引用,则该语句将失败
对于语句 CREATE TABLE ... LIKE 和 CREATE TABLE ... SELECT ,目标表保留原始表中的表达式默认值。
插入新行时,可以通过省略列名或将列指定为 DEFAULT 来插入具有表达式 default 的列的默认值(就像具有文字默认值的列一样)
mysql> CREATE TABLE t4 (uid BINARY(16) DEFAULT (UUID_TO_BIN(UUID())));
mysql> INSERT INTO t4 () VALUES();
mysql> INSERT INTO t4 () VALUES(DEFAULT);
mysql> SELECT BIN_TO_UUID(uid) AS uid FROM t4;
+--------------------------------------+
| uid |
+--------------------------------------+
| f1109174-94c9-11e8-971d-3bf1095aa633 |
| f110cf9a-94c9-11e8-971d-3bf1095aa633 |
+--------------------------------------+
# ================================================================
创建表时指定 DEFAULT 约束:
CREATE TABLE `lession` (
id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
name varchar(32) default '',
views int(11) NOT NULL default '0',
created_at DATETIME
);
ALTER TABLE 时的 SQL DEFAULT 约束:
当然可以使用modify来修改, 只不过用modify比较麻烦
ALTER TABLE lession ALTER [column] views SET DEFAULT '1';
删除 DEFAULT 约束:
ALTER TABLE lesson ALTER views DROP DEFAULT;
# ================================================================
# 3. unique唯一性约束
PRIMARY KEY 约束会自动定义一个 UNIQUE 约束,
或者说 PRIMARY KEY 是一种特殊的 UNIQUE 约束。
但二者是有明显区别的:
1. 每个表可以有多个 UNIQUE 约束,但只能有一个 PRIMARY KEY 约束
2. unique可以为空,primary不能为空
# =============================================================
CREATE TABLE 时的 SQL UNIQUE 约束 :
在创建表结构时,可以使用 UNIQUE 关键字给表添加 UNIQUE 约束
CREATE TABLE lesson (
id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
name varchar(32) default '',
views int(11) NOT NULL default '0',
created_at DATETIME,
UNIQUE [KEY](name)
);
or
create table t1(id int unique [key], depart char(5) unique [key]);
UNI # 默认的名字就是字段的名字
如果想要多加多列,可以在括号内添加列,并使用逗号 (,) 分隔
CREATE TABLE lesson (
id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
name varchar(32) default '',
views int(11) NOT NULL default '0',
created_at DATETIME,
unique (name,id)
);
MUL # 多列的默认名字,貌似是左边第一个字段,或者通过show index from lesson \G查看
如果还想给 UNIQUE 约束命名
1. create table t1(
id int, depart char(5),
unique uq_id (id),
unique uq_depart (depart)
);
2. create table t1(
id int,
depart char(5),
-- constraint uq_name unique(id, depart)
constraint unique uq_name (id, depart)
);
3. create table t1(
id int,
depart char(5),
unique uq_name (id, depart)
); MUL
ALTER TABLE 时的 SQL UNIQUE 约束:
如果表已经被创建,而又想添加 UNIQUE 约束,可以使用 ALTER TABLE ADD 命令
ALTER TABLE lesson ADD UNIQUE [KEY](name);
alter table lesson modify name char unique key;
当然了,我们的 UNIQUE 可以包含多列,添加方法就和建表时添加多列是同样的
ALTER TABLE lesson ADD UNIQUE (id,name);
如果还想给 UNIQUE 约束命名,可以使用 CONSTRAINT 关键字
alter table lesson add unique names (name);
ALTER TABLE lesson ADD [CONSTRAINT [uniq_lesson_name]] UNIQUE (id,name);
删除 UNIQUE 约束:
如果想要删除 UNIQUE 约束,可以使用 ALTER TABLE DROP 命令
ALTER TABLE lesson DROP {INDEX | key} uniq_lesson_name;
# =============================================================
# 跟primary key 的区别就是unique 可以为空
# not null + unique的化学反应
create table t3(id int not null unique, name char(5));
结果id为PRI,也就是主键
# =============================================================
# 4. primary key主键
primary key = not null + unique
主键primary key是innodb存储引擎组织数据的依据,
innodb称之为索引组织表,一张表中必须有且只有一个主键。
如果创建表的时候没有明确指定primary key,那么innodb会搜索一个not null + unique的字段作为主键;
如果没有符合条件的,则innodb会自动创建一个6 字节的 ROWID隐藏的字段作为主键,当然对我们没有什么意义。
1. 主键必须包含唯一的值
2. 主键列不能包含 NULL 值
3. 每个表都应该有一个主键,并且每个表只能有一个主键
# ================================================================================
CREATE TABLE 时的 SQL PRIMARY KEY 约束:
创建表时可以使用 PRIMARY KEY 关键字给表添加 PRIMARY KEY 约束,
但要注意,添加的列必须设置为 NOT NULL;(v8版本,其实不用设置not null,因为primary key = not null + unique)
CREATE TABLE lesson (
id int(11) NOT NULL [PRIMARY] KEY AUTO_INCREMENT, -- primary可以不用写
name varchar(32) default '',
views int(11) NOT NULL default '0',
created_at DATETIME
);
or 特别注意:primary key的key关键字,不能少;这也是跟unique不同的地方
create table pri_tb (id int, primary key(id));
虽然一个表只能有一个 PRIMARY KEY,但一个 PRIMARY KEY 可以包含多个列,
添加多个列可以使用 PRIMARY KEY,关键字,括号内添加多个列,多列之间用逗号分隔。
CREATE TABLE lesson (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(32) default '',
views int(11) NOT NULL default '0',
created_at DATETIME,
PRIMARY KEY (id,name)
);
如果需要给 PRIMARY KEY 约束命名,可以使用 CONSTRAINT 关键字:
1. create table t1(id int, name char(5), primary key pk_name (id));
2. CREATE TABLE lesson (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(32) default '',
views int(11) NOT NULL default '0',
created_at DATETIME,
[CONSTRAINT [pk_lesson_id]] PRIMARY KEY (id,name)
);
ALTER TABLE 时的 SQL PRIMARY KEY 约束:
如果一个表已经创建,而又想给表添加 PRIMARY KEY 约束,可以使用 ALTER TABLE 命令
alter table t1 modify id int primary key;
ALTER TABLE lesson ADD PRIMARY KEY (id);
ALTER TABLE lesson ADD CONSTRAINT pk_lesson PRIMARY KEY (id,name);
给 PRIMARY KEY 约束命名:
如果想要给 PRIMARY KEY 约束命名,可以使用 ALTER TABLE CONSTRAINT 命令
ALTER TABLE lesson ADD CONSTRAINT pk_lesson_id PRIMARY KEY (id);
删除 PRIMARY KEY 约束:
ALTER TABLE lesson DROP PRIMARY KEY
# ==================================================================================
# 5. auto_increment自增
# 前提:要想为该字段设置自增,就必须先设置一个键, 并且default不能和auto_increment同时出现
# 建议:建立一张表设置一个id字段并且为自增
create table t1(id int primary key auto_increment);
# 注意:
1. 将 0 存储到 AUTO_INCREMENT 列与存储 NULL 具有相同的效果,
除非启用了 NO_AUTO_VALUE_ON_ZERO SQL 模式。
2. AUTO_INCREMENT 序列以 1 开始而不是 0
3. 在 MySQL 8.0 及以上的版本, AUTO_INCREMENT 列会自动添加一个隐式的 UNSIGNED 属性来保证插入的值非负。
4. 整数或浮点数据类型可以添加附加属性 AUTO_INCREMENT。
如果某个列添加了 AUTO_INCREMENT 属性,那么在插入数据的时候,
如果不指定该列或者指定该列的值为 NULL or 0,
那么 MySQL 会自动将该列的值设置为下一个序列值
5. 在有自增和主键的情况下,想删除主键,得先删除自增再删除主键
删除自增:alter table tb4 modify id int not null;
# 更改表的自增值:
# alter table t11 auto_increment=10;
# 创建表的时指定:
# create table t11(id int auto_increment, primary key(id) auto_increment=10;
# 查看自增列的步长和起始值:
show variables like 'auto_inc%' 默认看的是会话级别的
# 设置自增列的步长和起始值:
# 1. 基于会话级别:
# MySQL是基于会话的,每一次登录就是一个会话,自增值也不一样,本次操作只针对当前会话生效
# show session variables like 'auto_inc%';
# set session auto_increment_increment=10; # 步长
# set session auto_increment_offset=2; # 起始值/起始偏移量
# PS:起始值 <= 步长
# 2. 基于全局级别:(需要重新登录)
# show global variables like 'auto_inc%';
# set global auto_increment_increment=5;
# set global auto_increment_offset=3;
# 最好不要设置全局的自增
##########################################################
小坑:如果有自增列,insert插入数据的时候,values(xx),(xx),(xx),前两个都符合规则,最后一个不符合,
# 虽然数据不会插入成功,但是自增值已经改变,此时再插入数据values(xx),自增值变成了 4
################################################################
# 6. foreign key外键
# 就是在一张表中存储另一张表的唯一字段的值
# 前提:存储引擎为innodb,且被关联表的那个字段必须唯一(unique / primary key)
# 建议:在实际开发中,尽量不要在数据库中的表与表之间建议硬性的关系,造成了强耦合的问题,不方便日后的扩展
# 应在自己的应用程序级别建立逻辑上的联系, 所以开发团队要有共识。
# 作用:
# 1. FOREIGN KEY 约束用于预防破坏表之间连接的行为
# 2. FOREIGN KEY 约束也能防止非法数据插入外键列,因为插入的数据必须是外键所指向的那个表字段中的值之一
# 两个作用简单明了
1. 当删除一个 FOREIGN KEY 指向的主表 (lesson) 记录时,如果 FOREIGN KEY 所在的表 (lesson_views) 存在记录,那么会删除失败
2. 当在 FOREIGN KEY 表 ( lesson_views ) 插入或更新一条记录,如果 FOREIGN KEY 指向的主表 ( lesson ) 不存在该记录,那么插入或者更新失败
# ================================================================================
CREATE TABLE 时的 SQL FOREIGN KEY 约束:
给一个表添加 FOREIGN KEY 约束可以使用 FOREIGN KEY 关键字
CREATE TABLE lesson_views (
uniq bigint(20) primary key NOT NULL default '0' ,
lesson_name varchar(32) default '',
lesson_id int(11) default '0',
date_at int(11) NOT NULL default '0',
views int(11) NOT NULL default '0',
FOREIGN KEY (lesson_id) REFERENCES lesson(id)
);
给 FOREIGN KEY 命名
如果想要给 FOREIGN KEY 约束命名,可以使用 CONSTRAINT 关键字
create table fk_tb(
fid int auto_increment not null primary key,
name char (5),
main_id int,
foreign key fk_name (main_id) references main_tb(id)
);
注意: 外键的类型 必须 跟关联的表的字段类型要一致, 最好后面的宽度也一样.
CREATE TABLE lesson_views (
uniq bigint(20) primary key NOT NULL default '0' ,
lesson_name varchar(32) default '',
lesson_id int(11) default '0',
date_at int(11) NOT NULL default '0',
views int(11) NOT NULL default '0',
CONSTRAINT fk_lesson_id FOREIGN KEY (lesson_id) REFERENCES lesson(id)
);
ALTER TABLE 时的 SQL FOREIGN KEY 约束:
如果一个表已经被创建,我们仍然可以使用 ALTER TABLE FOREIGN KEY 来添加外键约束
ALTER TABLE lesson_views ADD FOREIGN KEY (lesson_id) REFERENCES lesson(id);
如果还想给 FOREIGN KEY 约束命名,则可以像下面这样使用
ALTER TABLE lesson_views ADD CONSTRAINT fk_lesson_id FOREIGN KEY (lesson_id) REFERENCES lesson(id);
# 默认的名字: 表名 + _ + ibfk_1
# 表名, 小写
# ib, innodb
# fk, foreign key
# _1, 表中的第一个外键
删除 FOREIGN KEY 约束:
如果想要删除一个已经命名的 FOREIGN KEY 约束,可以使用 DROP 关键字
ALTER TABLE lesson_views DROP FOREIGN KEY fk_lesson_id;
# ================================================================================
# 创建步骤:
# 1. 首先要创建被关联的表:
create table department(
id int auto_increment primary key,
name char(10),
comment char(50)
) engine = innodb auto_increment = 1 default charset = utf8;
# 2. 再创建主动关联的表:
create table employee(
id int auto_increment,
name char(5),
dep_id int,
primary key(id),
constraint fk_name foreign key(dep_id) references department(id)
);
# 插入数据步骤:
# 1. 首先往被关联的表插入数据
insert into department(name, comment) values('技术', '技术能力有限部门'),
('销售', '销售能力不足部门'),
('财务', '花钱特别多部门');
# 2. 再往主动关联的表插入数据
insert into employee(name, dep_id) values('alex', 1),
('egon', 1),
('taiba', 1),
('peiqi', 2),
('eva-j', 3);
# 删除步骤:(不作特殊处理)(说明:这是在fk所在的表存在该记录时,才要先删主动关联表,再删被关联表)
# 1. 首先删除主动关联的表的数据
delete from employee where id = 1; (id为1的都要删干净)
# 2. 再删除被关联表的数据
delete from department where id = 1;
# 如果说我想更改被关联表的数据,同时,主动关联的表的数据也随之发生变化:
# (更新的时候,同步更新;删除的时候,同步删除)
# 创建主动关联表的时候需要加额外的参数:
create table employee(
id int auto_increment,
name char(10),
dep_id int,
primary key(id),
constraint fk_name foreign key(dep_id) references department(id)
on delete cascade
on update cascade
);
# 例如下拉框中的信息,就可以使用外键
# 可以将多个列组合成一个外键,前提是此表中要设置自增还有主键(在v8中, 我试了一下, 并不用),
并且与另一个表的类型要一致,要跟另一个表的主键挂钩
# 除此之外另一个表的主键是多列组合的,
# 外键的名字要唯一,不能与同一个数据库中的其他表的外键名字重复
# ========================================================================================
# 7. check约束
# CHECK 约束用于限制列中的值的范围
CHECK 约束既可以用于某一列也可以用于某张表:
1. 如果对单个列定义 CHECK 约束,那么该列只允许特定的值
2. 如果对一个表定义 CHECK 约束,那么此约束会基于行中其他列的值在特定的列中对值进行限制
# ===========================================================================================
CREATE TABLE 添加 CHECK 约束:
创建表结构时可以使用 CHECK 关键字给表或者字段添加 CHECK 约束
例如我们在创建 lesson 表时可以给 id 字段加上一个大于 0 的 约束
CREATE TABLE lesson (
id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
name varchar(32) default '',
views int(11) NOT NULL default '0',
created_at DATETIME,
CHECK ( id>0 )
);
多个字段添加约束:
如果想给一个表中多个字段添加约束,直接在 CHECK 关键字后的括号内添加,每个约束使用 AND 连接
CREATE TABLE lesson (
id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
name varchar(32) default '',
views int(11) NOT NULL default '0',
created_at DATETIME,
CHECK ( id>0 AND views >= 0 );
);
给 CHECK 约束命名:
CREATE TABLE lesson (
id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
name varchar(32) default '',
views int(11) NOT NULL default '0',
created_at DATETIME,
CONSTRAINT chk_lesson_id CHECK ( id>0 )
);
默认的名字: 表名 + _ + chk_1
ALTER TABLE 时的 SQL CHECK 约束:
如果表已经被创建,我们可以使用 ALTER TABLE ADD CHECK 添加约束
ALTER TABLE lesson ADD CHECK (id>0);
并不能alter table lesson add check 名字 (id > 0);
如果还想要命名 CHECK 约束,并定义多个列的 CHECK 约束
ALTER TABLE lesson ADD CONSTRAINT chk_lesson CHECK (id>0 AND views >= 0);
删除 CHECK 约束:
ALTER TABLE lesson DROP CHECK chk_lesson_id
最佳实战:
虽然各个数据库系统都可以添加 CHECK 约束,但我们不推荐你这么做,因为这会影响数据插入和更新的速度
# ============================================================================================
# 表与表之间的关系:
# 分析步骤:
# 1. 先站在左表的角度分析:
# 左表的多条记录是否可以对应右表的一条记录,如果是,则需要在左表新建一个foreign key 字段关联右表的一个唯一字段(通常是id)
# 2. 再站在右表的角度分析:
# 右表的多条记录是否可以对应左表的一条记录,如果是,则需要在右表新建一个foreign key 字段关联左表的一个唯一字段(通常是id)
# 总结:
# 多对一 / 一对多:
# 1. 如果只有步骤1成立,则表明是左表多对一右表
# 2. 如果只有步骤2成立,则表明是右表多对一左表
# 关联方式:foreign key即可
create table book(
id int primary key auto_increment,
name varchar(20),
press_id int not null,
foreign key(press_id) references press(id)
on delete cascade
on update cascade
);
# 在条件为 左表的多条记录可以对应右表的一条记录,反之不成立的情况下,则表明是左表多对一右表,需要在左表新建一个foreign key字段关联右表的一个唯一字段
# 上述将条件反过来的话,则表明是右表多对一左表,需要在右表新建一个foreign key字段关联左表的一个唯一字段
# 多对多:
# 如果步骤1和2同时成立,则证明这两张表示一个双向的多对一,
即多对多,需要定义一个存放二者关系的关系表
# 关联方式:foreign key + 一张新的表 (个人看例子得出:根据情况加联合主键)
# 新的表:
create table author2book(
id integer not null unique auto_increment,
author_id int not null,
book_id int not null,
constraint fk_author foreign key(author_id)
references author(id)
on delete cascade
on update cascade,
constraint fk_book foreign key(book_id)
references book(id)
on delete cascade
on update cascade,
primary key(author_id, book_id)
);
# 左表的多条记录可以对应右表的一条记录, 右表的多条记录也可以对应左表的一条记录,
# 则证明这两张表表示的是一个双向的多对一, 即多对多, 需要定义一张新的表来存放他们二者之间的关系
# 关联方式: foreign key + 一张新的表
# 一对一:
如果1和2都不成立,而是左表的一条记录唯一对应右表的一条记录,反之亦然。
这种情况很简单,就是在左表foreign key右表的基础上,将左表的外键字段设置成unique即可
# (个人心得:问题就在于在哪一方设置foreign key,我觉得就是在需要填数据的一方)
# 关联方式:foreign key + unique
create table student(
id int primary key auto_increment,
class_name varchar(20) not null,
customer_id int,
unique(customer_id),
constraint foreign key(customer_id)
references customer(id)
on delete cascade
on update cascade
);
#####################################################################################################
# 单表查询:
语法:
SELECT distinct 字段1, 字段2... FROM 表名
WHERE 条件
GROUP BY field
HAVING 筛选
ORDER BY field
LIMIT 限制条数;
优先级:
from
where
group by
having
select
distinct
order by
limit
1. 找到表:from
2. 拿着where指定的约束条件,去文件/表中取出一条条记录
3. 将取出的一条条记录进行分组group by,如果没有group by,则整体作为一组
4. 将分组的结果进行having过滤
5. 执行select
6. distinct去重
7. 将结果按条件排序:order by
8. limit限制结果的显示条数
# DISTINCT: 放在字段的前面,对其后所有的字段都生效,记录重复的只显示一次(记录去重)
# distinct 效率不高,如果能用其他方式实现,首选其他
# =============================================================
MySQL DISTINCT 的作用:
排除重复的行
(根据参数所指定的列排除重复的行)
DISTINCT 有带括号和没括号的两种用法,这两种是如何指定参数的呢 ?
我们使用过程中发现
1. 如果跟其它函数结合使用,那么只会使用小括号内的参数
2. 否则,那么 DISTINCT 关键字后的所有列都是参数
MySQL DISTINCT 的位置:
千万不要认为 DISTINCT 只能放在开头,也不要认为 DISTINCT 可以放在任意位置。
1. 单独的 DISTINCT 关键字只能放在开头,放在其它位置会报错
2. 如果是配合其它的函数使用,比如 COUNT(), 应该是其它函数可以任意位置时,DISTINCT 也可以任意位置
DISTINCT 中的小括号 ():
SQL 解析器会忽略 DISTINCT 关键字后面的小括号,而把 DISTINCT 关键字后面的所有列都作为唯一条件
SQL DISTINCT 的基本用法:
在日常使用 DISTINCT 关键字时,一般有以下几种
###注意: 请留意每种的列的数量###
1. 单独获取某一列不重复的值
这种情况下,有无小括号的结果是一样的
SELECT DISTINCT user FROM fruits;
SELECT DISTINCT(user) FROM fruits;
2. 单独获取某一列不重复值的数量
SELECT COUNT(DISTINCT(user)) FROM fruits;
3. 以多列作为条件获取不同的值
一定要记住,当你使用多列时,并不仅仅时使用小括号内的列,而是全部列
SELECT DISTINCT(user),fruit FROM fruits;
# ============================================================
# 计算字段,可以进行四则运算
# ======================================================================
字段一般是计算字段:
例如:
select 3 * 2;
select 3 * 2 as res;
SELECT name, salary*12 Annual_salary FROM employee;
select null * 1; -- null
# ========================================================================
# where子句:
# =======================================================================
WHERE 子句要在 FROM 子句之后给出
文本字段 vs 数值字段
1. SQL 使用单引号来环绕文本值
虽然大部分数据库系统也接受双引号,但我们极力反对使用双引号
2. 如果是数值字段,请不要使用引号
虽然使用数值字段也可以使用单引号,但数据库系统要经过一次数据类型转换,增加了数据库系统的开销
# where子句中的运算符:
运算符 描述
= 等于
<> 不等于
> 大于
< 小于
>= 大于等于
<= 小于等于
BETWEEN 在某个范围内
LIKE 搜索某种模式
IN 指定针对某个列的多个可能值
## 在 SQL 的一些版本中,<>操作符可被写成 != ##
# > / >= / < / <= / != / <> / = / <=> (<=>是MYSQL特有的)
安全等于<=>
1.可作为普通运算符的=
2.也可以用于判断是否是NULL 如:where salary is NULL/(is not NULL) ->where salary <=> NULL
# between ... and ... / not between .... and ....
# =====================================================================
SQL BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期
注意
不同的数据库中,BETWEEN 操作符会产生不同的结果
在某些数据库中,BETWEEN 选取介于两个值之间但不包括两个测试值的字段
在某些数据库中,BETWEEN 选取介于两个值之间且包括两个测试值的字段 -----> MySQL属于这种
在某些数据库中,BETWEEN 选取介于两个值之间且包括第一个测试值但不包括最后一个测试值的字段
因此,请检查你的数据库系统是如何处理 BETWEEN 操作符
例子:
1. 选取 id 介于 1 和 3 之间的所有课程
SELECT * FROM lesson WHERE id BETWEEN 1 AND 3;
2. 选取 id 不在 1 和 3 之间的所有课程
SELECT * FROM lesson WHERE id NOT BETWEEN 1 AND 3;
在 VARCHAR 等文本类型上使用 BETWEEN 操作符
1. 选取 name 以介于 'O' 和 'S' 之间字母开始的所有课程
SELECT * FROM lesson WHERE name BETWEEN 'O' AND 'S';
2. 选取 name 不介于 'O' 和 'S' 之间字母开始的所有课程
SELECT * FROM lesson WHERE name NOT BETWEEN 'O' AND 'S';
在 DATETIME 等日期类型列上使用 BETWEEN 操作符
1. 选取 created_at 介于 2017-04-18 16:03:32 和 2017-05-01 06:16:14 之间的数据
SELECT * FROM lesson WHERE created_at BETWEEN '2017-04-18 16:03:32' AND '2017-05-01 06:16:14';
最佳实战:
一般情况下,我们不推荐使用 BETWEEN
为什么呢?
因为 BETWEEN 并不是所有开发者都熟悉,而且不同数据库实现有不一样的实现
那我们可以用什么代替呢?
我们可以用 > 或 < 代替,比如下面的 SQL 语句选取 id 介于 1 和 3 之间的所有课程
SELECT * FROM lesson WHERE id >=1 AND id <=3;
# ==========================================================================
# not > and > or
# in / not in
# =========================================================
SQL IN 操作符用于在 WHERE 子句中限制一列只能包含有限个值
例如:
SELECT * FROM lesson WHERE id IN (1,3);
where id in (select id from t11);
# ===========================================================
# is / is not null (判断某个字段是否为NULL,不能用等号,需要用IS)
# like / not like
# rlike / not rlike
# regexp / not regexp
# =============================================================================
#####################################################################################
#1:子查询是将一个查询语句嵌套在另一个查询语句中。
#2:内层查询语句的查询结果,可以为外层查询语句提供查询条件。
#3:子查询中可以包含:IN、NOT IN、ANY、ALL、EXISTS 和 NOT EXISTS等关键字
#4:还可以包含比较运算符:= 、 !=、> 、<等
EXISTS关字键字表示存在。在使用EXISTS关键字时,内层查询语句不返回查询的记录。
而是返回一个真假值。True或False
当返回True时,外层查询语句将进行查询;当返回值为False时,外层查询语句不进行查询
drop table if exists department;
select * from employee where exists (select id from department where id=204);
select * from employee where not exists (select id from department where id=204);
######################################################################################
# ==================================================================
MySQL在Linux下数据库名、表名、列名、别名大小写规则是这样的:
1、数据库名与表名是严格区分大小写的;
2、表的别名是严格区分大小写的;
3、列名与列的别名在所有的情况下均是忽略大小写的;
4、变量名也是严格区分大小写的;
MySQL在Windows下都不区分大小写!!!
Mysql默认的字符检索策略:
*_bin: 表示的是binary case sensitive collation,也就是说是区分大小写的
*_cs: case sensitive collation,区分大小写 注意:在Mysql5.6.10版本中,不支持utf8_genral_cs!!!!
*_ci: case insensitive collation,不区分大小写
1. 创建表时,直接设置表的collate属性为utf8mb4_general_cs或者utf8mb4_bin;
2. 如果已经创建表,则直接表的collate
3. 修改字段, 添加binary约束
ALTER TABLE TABLENAME MODIFY COLUMN COLUMNNAME VARCHAR(50) BINARY CHARACTER SET utf8 COLLATE
utf8_bin DEFAULT NULL;
4. 对某字段手动指定binary约束
CREATE TABLE NAME(
name VARCHAR(10) BINARY
);
5. 在每一个条件前加上binary关键字
select * from user where binary username = 'admin' and binary password = 'admin';
6. 将参数以binary('')包围
select * from user where username = binary('clip');
# =======================================================================
# like子句:
# =====================================================================
SQL 中的 LIKE 子句用于在 WHERE 子句中搜索列中的指定模式
SELECT column_name(s) FROM table_name WHERE column_name LIKE pattern;
pattern 是一个合法的模式字符串,有很多种 模式,但最常用的也是最容易记住的就是百分号 ( % ) 可以代替任意字符
SQL LIKE 操作符:
1. 如果我们不使用任何通配符,那么 LIKE 的效果相当于 = 操作符
SELECT * FROM lession WHERE name LIKE 'Python 基础教程';
2. 因为百分号 ( % ) 可以代替任何任意数量的字符,所以下面的 SQL 语句选取 name 以 S 开头的课程
SELECT * FROM lession WHERE name LIKE 'S%';
3. 下面的 SQL 语句选取 name 以字符串 "教程" 结尾的所有课程
SELECT * FROM lession WHERE name LIKE '%教程';
4. 下面的 SQL 语句选取 name 包含 y 字母的课程
SELECT * FROM lession WHERE name LIKE '%y%';
5. 通过使用 NOT 关键字,我们可以选取不匹配模式的记录
下面的 SQL 语句选取 name 不包含 "y" 的所有课程
SELECT * FROM lession WHERE name NOT LIKE '%y%';
# =====================================================================
# 通配符: 特别注意(默认情况下, windows下的MySQL什么都不分大小写, 包括, 查询参数)
# ======================================================================
SQL 通配符:
SQL 中,通配符与 SQL LIKE 操作符一起使用,可用于替代字符串中的任何其他字符
SQL 中规定可以使用以下通配符:
通配符 描述
% 替代 0 个或多个字符
_ 替代一个字符
[charlist] 字符列中的任何单一字符
[^charlist]或[!charlist] 不在字符列中的任何单一字符
使用 SQL % 通配符:
SELECT * FROM lession WHERE name LIKE 'S%';
使用 SQL _ 通配符:
选取 name 以一个任意字符开始,然后是 "ython" 的所有课程
SELECT * FROM lession WHERE name LIKE '_ython%';
选取 name 以 "S" 开始,然后是一个任意字符,然后是 "a",然后是一个任意字符,然后是 "a" 的所有课程
SELECT * FROM lession WHERE name LIKE 'S_a_a%';
使用 SQL [charlist] 通配符:
MySQL 中使用 REGEXP 或 NOT REGEXP 运算符 (或 RLIKE 和 NOT RLIKE) 来操作正则表达式
1. 选取 name 以 "P"、"S" 开始的课程
SELECT * FROM lession WHERE name REGEXP '^[PS]';
SELECT * FROM employee WHERE name REGEXP '^ale';
SELECT * FROM employee WHERE name REGEXP 'on$';
SELECT * FROM employee WHERE name REGEXP 'm{2}';
其它:
如果字段是整型,也能使用like
可以按照任意字段分组,但是分组完毕后,比如group by post,只能查看post字段,
如果想查看组内信息,需要借助于聚合函数
1. 查看MySQL 5.7默认的sql_mode
select @@global.sql_mode;
在ONLY_FULL_GROUP_BY模式下,
target list中的值要么是来自于聚集函数的结果,
要么是来自于group by list中的表达式的值。
set global sql_mode='STRICT_TRANS_TABLES,
NO_ZERO_IN_DATE,
NO_ZERO_DATE,
ERROR_FOR_DIVISION_BY_ZERO,
NO_AUTO_CREATE_USER,
NO_ENGINE_SUBSTITUTION';
执行select * from employee group by post;
set global sql_mode='ONLY_FULL_GROUP_BY';
范例:
统计各个课程的总访问量
SELECT lesson_name, SUM(views) FROM lesson_views GROUP BY lesson_name;
GROUP BY X, Y意思是将所有具有相同X字段值和Y字段值的记录放到一个分组里。
SQL GROUP BY 多表连接
SELECT lesson.name,SUM(lesson_views.views)
FROM lesson,lesson_views
WHERE lesson.id=lesson_views.lesson_id
GROUP BY lesson.name;
强调:
(在单表下)如果我们用unique的字段作为分组的依据,则每一条记录自成一组,这种分组没有意义
多条记录之间的某个字段值相同,该字段通常用来作为分组的依据
SELECT post,GROUP_CONCAT(name) as emp_members FROM employee GROUP BY post;
SQL 中的 HAVING 子句用于筛选分组 ( GROUP BY ) 后的各组数据,相当于 SELECT 语句中的 WHERE 语句
HAVING 子句一般跟在 GROUP BY 子句后面
可以看作是where做了第一次筛选,having进行第二次筛选
SQL HAVING 范例:
选择总访问量在 100 以内的课程
SELECT lesson_name, SUM(views) as total_views
FROM lesson_views
GROUP BY lesson_name
HAVING total_views < 100;
又学习了:
select lesson_name, sum(views) as total_views
from lesson_views
group by lesson_name
having sum(views) < 100;
HAVING与WHERE不一样的地方在于!!!!!!
可以看作where做了第一次筛选,having进行第二次筛选
1. SQL 中允许临时给表名或列名称指定别名,创建别名是为了让列名称的可读性更强
2. 别名只是当前 SQL 语句执行过程中临时的改变,在数据库中实际的表的名称不会改变
3. SQL 中创建别名使用 AS 关键字, 可以省略
4. 如果列名称包含空格,要求使用引号, 最好使用单引号
5. 在MySQL中,可以在ORDER BY,GROUP BY和HAVING子句中使用列别名来引用该列
6. 不能在WHERE子句中使用列别名。(使用表别名是允许的)
原因是当MySQL评估求值WHERE子句时,SELECT子句中指定的列的值可能尚未确定。
列的 SQL 别名
SELECT column_name AS alias_name FROM table_name;
SELECT lesson_id as lid, lesson_name as name, date_at, views FROM lesson_views;
表的别名
SELECT column_name(s) FROM table_name AS alias_name;
SELECT * FROM lesson_views as lv WHERE lv.lesson_id = 2;
最佳实战:
如果出现以下几种情况之一,使用别名很有用:
1. 在查询中涉及超过一个表
2. 在查询中使用了函数
3. 列名称很长或者可读性差
4. 需要把两个列或者多个列结合在一起
SQL 函数
任何一个数据库系统都内置了数量相当可观的又非常实用的小函数
这些函数可以根据实现功能的不同划分为不同的类,当然,除了很明显的日期时间和字符串函数两大类外
我们还可以把这些函数归纳为两大类: Aggregate 函数 和 Scalar 函数
这两个英文单词,字面理解它的意思即可
SQL Aggregate 函数
SQL Aggregate 函数用于计算从列中取得的值,并返回一个单一的值
常用的 Aggregate 函数(聚合函数)有:
函数 说明
AVG() 返回平均值
COUNT() 返回行数
MAX() 返回最大值
MIN() 返回最小值
SUM() 返回总和
avg() 忽略列值为NULL的行
count()
根据参数的不同,COUNT() 大致有三种用法
1. COUNT ( column_name )
COUNT(column_name) 函数返回指定列的值的数目,NULL 值除外
SELECT COUNT(column_name) FROM table_name;
2. COUNT(*)
COUNT(*) 函数返回表中的记录数,包括 NULL 值
SELECT COUNT(*) FROM table_name;
3. COUNT(DISTINCT column_name )
COUNT(DISTINCT column_name) 函数返回指定列的不同值的数目
SELECT COUNT(DISTINCT column_name) FROM table_name;
SQL Scalar 函数
SQL Scalar 函数基于输入值,返回一个单一的值
常用的 Scalar 函数有:
函数 说明
UCASE() 将某个字段转换为大写
LCASE() 将某个字段转换为小写
MID() 从某个文本字段提取字符,MySQL 中使用
SELECT MID( column_name ,start[,length]) FROM table_name;
参数 描述
column_name 必需。要提取字符的字段
start 必需。设置开始位置 ( 起始值是 1 ),这个一定要注意
length 可选。要返回的字符数。如果省略,则 MID() 函数返回剩余文本
SubString(fieldname,1,end) 从某个文本字段提取字符
LENGTH() 返回某个文本字段的长度
LENGTH() 返回的是数据库服务器编码下的字符串长度,
如果数据库服务器的编码是 UTF-8,那么 '中国' 将返回 6
ROUND() 对某个数值字段进行指定小数位数的四舍五入
ROUND( column_name ,decimals)
参数 描述
column_name 必需。要舍入的数值或字段
decimals 可选。设置要返回的小数位数。默认为 0
NOW() 返回当前的系统日期和时间
范例:
select ucase('abc');
select lcase('ABC');
SELECT MID('www.twle.cn',3,4);
SELECT LENGTH('www.twle.cn 基础教程');
INSERT INTO lession(name, views, created_at) VALUES('SQL 基础教程', 0, NOW());
说明:
一般情况下我们不推荐使用UCASE LCASE MID LENGTH函数,
因为应用的瓶颈一般在数据库,这些显然会降低数据库的并发能力
SQL ORDER BY 关键字用于对结果集按照一个列或者多个列进行排序
SELECT column_name(s) FROM table_name ORDER BY column_name [ASC|DESC] [, column_name [ASC|DESC]];
SQL 排序有几个重点:
1. ORDER BY 关键字默认按照升序对记录进行排序
如需要按照降序对记录进行排序,可以使用 DESC 关键字
2. 如果没有 ORDER BY 语句,结果集会以 主键 升序排序
一般情况下,主键都是 id
多列排序,那么每个排序字段使用逗号 ( , ) 分隔
按照排序字段从左往右 ( id, age )
如果第一个排序字段 ( id ) 的值不一样,则按照第一个排序字段的值排序
如果第一个排序字段 ( id ) 的值一样,则按照第二个排序字段 ( age ) 的值排序
以此类推
用于限制返回结果集的条数
因为LIMIT的机制是每次都是从头开始扫描,如果需要从第60万行开始,读取3条数据
就需要先扫描定位到60万行,然后再进行读取,而扫描的过程是一个非常低效的过程。
所以,对于大数据处理时,是非常有必要在应用层建立一定的缓存机制
(现在的大数据处理,大都使用缓存)
表连接有啥作用呢?
当我们的数据横跨 2 个或 2 个以上的表时,我们就要考虑要怎么排列这两个表中的数据,好让它们组成一个大的表
数据库系统把这种多个表数据的排列方式叫做表连接 ( SQL JOIN )
select * from tb1 [inner] join tb2 on tb1.id = tb2.id;
等同于
select * from tb1, tb2 where tb1.id = tb2.id;
这种形式为内联结(不显示值为Null的行)
select * from tb1 left [outer] join tb2 on tb1.id = tb2.id;
在内连接的基础上增加左边有右边没有的和右边有左边没有的结果
SQL UNION 操作符合并两个或多个 SELECT 语句的结果
UNION 结果集中的列名总是等于 UNION 中第一个 SELECT 语句中的列名
1. 不允许重复 union
SELECT column_name(s) FROM table1 UNION SELECT column_name(s) FROM table2;
2. 允许重复值 union all
SELECT column_name(s) FROM table1 UNION ALL SELECT column_name(s) FROM table2;
但这些 SELECT 语句 的结果集必须符合一定的要求:
1. 每个 SELECT 语句必须拥有相同数量的列
2. 列也必须拥有相似的数据类型
3. 每个 SELECT 语句中的列的顺序必须相同
union 上下联表,自动去重,all选项可保留重复的行
select * from employee left join department on employee.dep_id = department.id
union
select * from employee right join department on employee.dep_id = department.id
;
一 SELECT语句关键字的定义顺序
SELECT DISTINCT <select_list>
FROM <left_table>
<join_type> JOIN <right_table>
ON <join_condition>
WHERE <where_condition>
GROUP BY <group_by_list>
HAVING <having_condition>
ORDER BY <order_by_condition>
LIMIT <limit_number>;
二 SELECT语句关键字的执行顺序
(7) SELECT
(8) DISTINCT <select_list>
(1) FROM <left_table>
(3) <join_type> JOIN <right_table>
(2) ON <join_condition>
(4) WHERE <where_condition>
(5) GROUP BY <group_by_list>
(6) HAVING <having_condition>
(9) ORDER BY <order_by_condition>
(10) LIMIT <limit_number>
SQL 视图 ( Views )
其本质是【根据SQL语句获取动态的数据集,并为其命名】,
用户使用时只需使用【名称】即可获取结果集,可以将该结果集当做表来使用。
视图的特征:
1. 视图总是显示最新的数据
2. 每当用户查询视图时,数据库引擎通过使用视图的 SQL 语句重建数据
SQL CREATE VIEW 创建视图:
CREATE VIEW view_name AS SELECT column_name(s) FROM table_name WHERE condition;
说明: AS 关键字后面的 SQL 语句可以是任何合法的 SQL SELECT 语句
例如: create view view_one as (select student_id, num from score);
从视图中创建视图:
非常有意思的是,可以从一个视图中创建另一个视图:
CREATE VIEW lesson_all_views AS SELECT sum(views) as views FROM lession_total_view;
查看当前数据库中所有的视图:
视图在数据库中类似于表的存在,所以,可以使用 show tables; 语句查看所有的视图
show tables;
SQL 修改视图:
很多人都会把这个翻译成 更新视图,我觉得吧,有点不妥,因为很容易和 更新表 联系起来
修改视图的意思,其实只能修改 AS 后面的 SQL 查询语句
如果要修改一个视图,可以使用 CREATE OR REPLACE VIEW 关键字
1. CREATE OR REPLACE VIEW view_name AS SELECT column_name(s) FROM table_name WHERE condition
2. alter view view_one as (select student_id, course_id, num from score);
SQL 删除视图:
DROP VIEW view_name;
例如: drop view view_one;
但是这么做效率并不高,还不如我们写子查询的效率高
那么意味着,一旦sql需要修改且涉及到视图的部分,则必须去数据库中进行修改,
而通常在公司中数据库有专门的DBA负责,
你要想完成修改,必须付出大量的沟通成本DBA可能才会帮你完成修改,极其地不方便
CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW
BEGIN
...
END
CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW
BEGIN
...
END
CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW
BEGIN
...
END
CREATE TRIGGER tri_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW
BEGIN
...
END
CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW
BEGIN
...
END
CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW
BEGIN
...
END
delimiter
CREATE TRIGGER tri_after_insert_cmd AFTER INSERT ON cmd FOR EACH ROW
BEGIN
IF NEW.success = 'no' THEN
INSERT INTO errlog(err_cmd, err_time) VALUES(NEW.cmd, NEW.sub_time) ;
END IF ;
END
delimiter ;
drop trigger tri_before_insert_cmd;
如果test和valN相等,则返回resultN,否则返回default
如果test是真,返回t;否则返回f
例如: select lession_name, if(isnull(views), 1, 0) from lession_views;
如果arg1不是空值,返回arg1,否则返回arg2
例如: SELECT lession_name,IFNULL( views,0) + 1000 FROM lession_views;
如果arg1=arg2返回NULL;否则返回arg1
user()
database()
bin()
hex()
oct()
soundex()
binary() 区分大小写
charset()
char_length() 以字符为单位
length() 以字节为单位
concat() 字符串拼接,如果有一个参数为NULL,则返回值为NULL
concat_ws() 可以指定连接符,忽略NULL,但不会忽略任何空字符串; concat_ws('-', 1, 2, 3)
group_concat()
cast() 例如: cast(num as char)
conv(num, c_sys, to_sys) 进制转换
format(n,d) 将数字n以'#,###,###.###'的字符串形式返回,以四舍五入的方式保留小数点后d位,若d为0,则表示结果没有小数部分
upper() 将文本的字母部分转化为大写
lower() 将文本的字母部分转化为小写
ucase()
lcase()
insert(str, pos, len, newstr)
replace(str, oldstr, newstr) 返回str中的oldstr被newstr替换后的最终字符串。如果oldstr不在里面,返回原str
left(str, len) 返回从str开头开始的len长度字符串 left('abc', 2)
right(str, len) 返回从str末尾开始的len长度字符串 right('abc', 2)
mid()
substring(str, pos, len) 取str的pos位置开始返回len长度的字符串
substring(str from pos for len)
substring(str from -pos for len)
substring(str, pos)
substring(str from pos)
substring(str from -pos)
instr(str, substr) 返回substr在str中出现的第一个位置。若没有,返回0
locate(substr, str, pos) 返回substr在str中从pos开始的第一个索引位置,pos可以没有;如果没有,返回0
repeat(str, count) 返回count*str组成的字符串。如果count或者str为NULL,结果为NULL;如果count为0,结果为空字符串
space(n) 返回一个由n个空格组成的字符串
reverse(str) 返回倒序的str
ltrim() 剔除字符串左边的空白字符
rtrim() 剔除字符串右边的空白字符
trim() 剔除字符串两边的空白字符 =
trim(both ' ' from str)
trim(leading 'x' from str)
trim(trailing 'x' from str)
返回0到1内的随机值,可以通过提供一个参数(种子)使RAND()随机数生成器生成一个指定的值。
加密函数
MD5()
计算字符串str的MD5校验和
PASSWORD(str)
返回字符串str的加密版本,这个加密过程是不可逆转的,和UNIX密码加密过程使用不同的算法。
curdate() / current_date()
curtime() / current_time()
current_timestamp()
now()
例子: create table employee(d date, t time, dt datetime);
insert employee values(now(), now(), now());
EXTRACT() 函数
MySQL EXTRACT() 函数返回日期/时间的单独部分,比如年、月、日、小时、分钟
EXTRACT(unit FROM date):
参数 说明
date 一个合法的日期表达式
unit 返回值的类型
unit 参数可以是以下值
值 说明
MICROSECOND 毫秒
SECOND 秒
MINUTE 分
HOUR 时
DAY 天
WEEK 周
MONTH 月
QUARTER 季度
YEAR 年
SECOND_MICROSECOND 秒.毫秒
MINUTE_MICROSECOND 分.秒.毫秒
MINUTE_SECOND 分.秒
HOUR_MICROSECOND 小时.分.秒.毫秒
HOUR_SECOND 小时.分.秒
HOUR_MINUTE 小时.分
DAY_MICROSECOND 小时.分.秒.毫秒
DAY_SECOND 小时.分.秒
DAY_MINUTE 小时.分钟
DAY_HOUR 小时
YEAR_MONTH 年.月
忽略说明中的点号 (.)
当 unit 为复合类型时,它就是把这些元数据简单的拼接在一起
DATE_ADD() 函数
MySQL DATE_ADD() 函数用于向日期添加指定的时间间隔
DATE_ADD(date,INTERVAL expr type)
参数 说明
date 合法的日期表达式
expr 希望添加的时间间隔
type 时间间隔的类型
type 参数可选值如下
值 说明
MICROSECOND 毫秒数
SECOND 秒数
MINUTE 分钟数
HOUR 小时数
DAY 天数
WEEK 周数
MONTH 月数
QUARTER 季度数
YEAR 年数
SECOND_MICROSECOND 秒.豪秒
MINUTE_MICROSECOND 分.豪秒
MINUTE_SECOND 分.秒
HOUR_MICROSECOND 小时.豪秒
HOUR_SECOND 小时.秒
HOUR_MINUTE 小时.分
DAY_MICROSECOND 天.豪秒
DAY_SECOND 天.秒
DAY_MINUTE 天.分钟
DAY_HOUR 天.小时
YEAR_MONTH 年.月
例如:
1. SELECT DATE_ADD(NOW(),INTERVAL 30 DAY );
2. 给当前时间加上 1天又1小时
SELECT NOW(), DATE_ADD(NOW(),INTERVAL 1.1 DAY_HOUR );
DATE_SUB() 函数
MySQL DATE_SUB() 函数从日期减去指定的时间间隔
DATE_SUB(date,INTERVAL expr type)
DATEDIFF() 函数
MySQL DATEDIFF() 函数返回两个日期之间的天数,准确的说,是返回两个日期时间午夜 ( 00:00:00 ) 的数目
DATEDIFF(enddate,startdate)
如果 enddate 大于 startdate ,那么返回的是正数,反之,返回的是负数
参数说明
参数 说明
enddate 开始时间,必须是合法的日期或日期/时间表达式
startdate 结束时间,必须是合法的日期或日期/时间表达式
例如:
1. 假设我们要返回日期 2017-06-19 到 2017-05-18 之间的天数,
SELECT DATEDIFF('2017-06-19','2017-05-18') AS days;
如果我们把两个时间对调一下,那么返回的结果就是负数
SELECT DATEDIFF('2017-05-18','2017-06-19') AS days;
year()
month()
day()
hour()
minute()
second()
microsecond()
date()
time()
week()
quarter()
monthname()
dayname()
dayofyear()
dayofweek() 返回日期星期几的索引 (1 = Sunday, 2 = Monday, ., 7 = Saturday);这些索引值对应于ODBC标准。
dayofmonth()
date_format(timestr, 日期时间占位符)
参数 说明
date 合法的日期
format 日期/时间的输出格式
format 参数可使用的元字符有
元字符 说明
%Y 年,4 位
%y 年,2 位
%m 月,数值 ( 00-12 )
%c 月,数值 ( 0 - 12)
%d 月的天,数值 ( 00-31 )
%e 月的天,数值 ( 0-31 )
%D 带有英文后缀的月中的天
%H 小时 ( 00-23 )
%h 小时 ( 01-12 )
%I 小时 ( 01-12 )
%k 小时 ( 0-23 )
%l 小时 ( 1-12 )
%i 分钟,数值 ( 00-59 )
%S 秒 ( 00-59 )
%s 秒 ( 00-59 )
%f 微秒
%p AM 或 PM
%r 时间,12-小时 ( hh:mm:ss AM 或 PM )
%T 时间, 24-小时 ( hh:mm:ss )
%j 年的天 ( 001-366 )
%w 周的天 ( 0=星期日, 6=星期六 )
%a 缩写星期名
%W 全称星期名
%b 缩写月名
%M 全称月名
%U 周 ( 00-53 )星期日是一周的第一天
%u 周 ( 00-53 )星期一是一周的第一天
%V 周 ( 01-53 )星期日是一周的第一天,与 %X 使用
%v 周 ( 01-53 )星期一是一周的第一天,与 %x 使用
%X 年,其中的星期日是周的第一天,4 位,与 %V 使用
%x 年,其中的星期一是周的第一天,4 位,与 %v 使用
例子:博客园中的博客多少年多少月发布了几篇博客
select data_format(sub_time, '%Y-%m'), count(1) from blog group by data_format(sub, '%Y-%m');
占位符一部分跟Python里面的一样
https:
select upper('egon');
select UPPER('egon') into @res;
SELECT @res;
更多信息:
https:
delimiter
create function f1(i1 int, i2 int)
returns int
begin
declare num int default 0;
set num = i1 + i2;
return(num)
end
delimiter ;
使用存储过程的优点:
使用存储过程的缺点:
补充:程序与数据库结合使用的三种方式
MySQL:存储过程
程序:调用存储过程
MySQL:
程序:纯SQL语句
MySQL:
程序:类和对象,即ORM(本质还是纯SQL语句)
MySQL:不放存储过程
程序员:将SQL语句写在代码中(可能会将SQL语句交给DBA看,如果合格了,就直接写在代码中;不合格的话,DBA提出建议,然后放在程序中)
或者是MySQL进行设置一个慢日志,意思是:如果SQL执行的速度慢于一定的速度,DBA查看这个日志,将指定的SQL进行修改,交给程序员
规模比较大的公司
MySQL:不放存储过程
程序员:类+对象 (但内部还是转化成了SQL语句)
MySQL中:
delimiter
create procedure p1()
BEGIN
select * from student;
select * from score where class_id = 1;
update student set sname = '牛奶' where sname = '理解';
END
delimiter ;
MySQL执行:
call p1();
Python中执行:
cursor.callproc('p1')
删除:
drop procedure [if exists] p1;
参数的类型有三种:
in:仅用于传参用
out:仅用于返回值
inout:既可用于传参,又可用于返回值
创建:
create PROCEDURE p1(in num1 int, in num2 int)
begin
select * from student where sid > num1;
select repeat('abc', num2);
end
执行:
call p1(1, 4)
Python中执行:
cursor.callproc('p1', (1, 4))
创建二:
create PROCEDURE p1(in num1 int, out num2 int)
begin
select * from student where sid > num1;
set num2 = 123123;
end
执行:
set @v1=1;
call p1(1, @v1);
select @v1;
Python中执行:
ret = cursor.callproc('p1', (1, 3))
print(cursor.fetchall())
cursor.execute('select @_p1_0, @_p1_1')
print(cursor.fetchall()
创建三:
delimiter
create procedure p2(
in n1 int,
inout n3 int,
out n2 int
)
begin
declare temp1 int ;
declare temp2 int default 0;
select * from student;
set n2 = n1 + 100;
set n3 = n3 + n1 + 100;
end
delimiter ;
执行:
set @v2 = 1;
set @v3 = 3;
call p2(100, @v3, @v2);
select @v2, @v3;
create procedure p6(out p_status_code tinyint)
BEGIN
declare exit handler for sqlexception
BEGIN
set p_status_code = 1;
rollback;
END;
DECLARE exit handler for sqlwarning
BEGIN
set p_return_code = 2;
rollback;
END;
start transaction;
delete from transaction_test;
insert into student(gender, class_id, sname)values('女', 2, 'belle');
commit;
set p_status_code = 2;
END;
set @status_code = 0;
call p6(@status_code);
select @status_code;
create procedure p8()
begin
declare row_id int;
declare row_num int;
declare temp int;
declare done int default false;
declare my_cursor cursor for select id, num from transaction_test;
declare continue handler for not found set done = true;
open my_cursor;
xxoo: loop
fetch my_cursor into row_id, row_num;
if done then
leave xxoo;
end if;
set temp = row_id + row_num;
insert into tran_test_B(number) values(temp);
end loop xxoo;
close my_cursor;
end;
create procedure p9(in username varchar(255), in passcode varchar(255))
BEGIN
set @username = username;
set @passcode = passcode;
prepare pre_sql from 'select * from userinfo where uname = ? and upwd = ?';
execute pre_sql using @username, @passcode;
deallocate prepare pre_sql;
end;
1. 叶子结点是否存放的是一整行的信息
2. 一张表中聚集索引只能有一个,辅助索引可以有多个
就是表记录的排列顺序和索引的排列顺序一模一样。
聚集索引,不会有单独的空间存储索引数据,而是在存储数据的时候就已经根据索引排好序。
通过表的主键来生成b+树,主键索引就属于 聚合索引
好处:
1. 对主键的排序查找和范围查找非常快,叶子结点存放的是数据一整行的信息。
2. 范围查询,即如果想查找主键某一范围内的数据,通过叶子结点的上层中间节点就可以得到页的范围,之后直接读取数据即可。
用另外的空间存储了记录的顺序,但是记录本身的物理顺序可以和索引不一样
表中除了聚集索引以外的都是辅助索引(非聚集索引)
例如:唯一索引 和 普通索引 都是非聚集索引
加速查找
主键索引
唯一索引
普通索引
1. 普通索引允许重复的值,就是两个记录的索引字段的值可以重复
2. 唯一索引不允许重复的值,两个记录的索引字段不允许重复,
其实 主键索引 也是一种特殊的唯一索引
select name from t1 where id = 20 命中了辅助索引但是未覆盖到索引,还需要从聚集索引中查找name
select id name from t1 where id = 20 命中索引 一次就找到 覆盖到了索引
加速查找
主键索引PRIMARY KEY:
加速查找+约束(不为空、不能重复)
唯一索引UNIQUE:
加速查找+约束(不能重复)
联合索引:
-PRIMARY KEY(id,name):联合主键索引
-UNIQUE(id,name):联合唯一索引
-INDEX(id,name):联合普通索引
举个例子来说,比如你在为某商场做一个会员卡的系统。
这个系统有一个会员表
有下列字段:
会员编号 INT
会员姓名 VARCHAR(10)
会员身份证号码 VARCHAR(18)
会员电话 VARCHAR(10)
会员住址 VARCHAR(50)
会员备注信息 TEXT
那么这个 会员编号,作为主键,使用 PRIMARY
会员姓名 如果要建索引的话,那么就是普通的 INDEX
会员身份证号码 如果要建索引的话,那么可以选择 UNIQUE (唯一的,不允许重复)
会员备注信息 , 如果需要建索引的话,可以选择全文搜索。
用于搜索很长一篇文章的时候,效果最好。
用在比较短的文本,如果就一两行字的,普通的 INDEX 也可以。
但其实对于全文搜索,我们并不会使用MySQL自带的该索引,而是会选择第三方软件如Sphinx,专门来做全文搜索。
hash类型的索引:查询单条快,范围查询慢
btree类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它)
InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
1、普通索引
普通索引仅有一个功能:加速查询
创建表 + 索引
create table in1(
nid int not null auto_increment [primary] key,
name varchar(32) not null,
email varchar(64) not null,
extra text,
key ix_name (name)
)
创建索引
create index index_name on table_name(column_name)
删除索引
drop index index_name on table_name;
alter table table_name drop {index | key} index_name;
查看索引
show index from table_name;
2、唯一索引
唯一索引有两个功能:加速查询 和 唯一约束(可含null)
create table in1(
nid int not null auto_increment primary key,
name varchar(32) not null,
email varchar(64) not null,
extra text,
unique ix_name (name)
)
创建索引
create unique index 索引名 on 表名(列名)
删除索引
drop index 索引名 on 表名
alter table table_name drop {index | key} index_name;
3. 主键索引
主键有两个功能:加速查询 和 唯一约束(不可含null)
create table in1(
nid int not null auto_increment [primary] key,
name varchar(32) not null,
email varchar(64) not null,
extra text,
index ix_name (name)
)
OR
create table in1(
nid int not null auto_increment,
name varchar(32) not null,
email varchar(64) not null,
extra text,
primary key(ni1),
index ix_name (name)
)
创建主键
alter table 表名 add primary key(列名);
删除主键
alter table 表名 drop primary key;
4. 组合索引 (默认名字为第一个字段名)
组合索引是将n个列组合成一个索引
其应用场景为:频繁的同时使用n列来进行查询,如:where n1 = 'alex' and n2 = 666。
create table in3(
nid int not null auto_increment primary key,
name varchar(32) not null,
email varchar(64) not null,
extra text
)
create index ix_name_email on in3(name,email);
rences users2(id), foreign key(rid) references role(id));
比如create index idx on s1(id);会扫描表中所有的数据,然后以id为数据项,创建索引结构,存放于硬盘的表中。
建完以后,再查询就会很快了。
MySAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。
而在innodb中,表数据文件本身就是按照B+Tree(BTree即Balance True)组织的一个索引结构,
这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此innodb表数据文件本身就是主索引。
因为inndob的数据文件要按照主键聚集,所以innodb要求表必须要有主键(Myisam可以没有),
如果没有显式定义,则mysql系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,
则mysql会自动为innodb表生成一个隐含字段作为主键,这字段的长度为6个字节,类型为长整型.
1. 能用数字类型就不用字符类型
2. char代替varchar
3. 固定长度的放在前面
4. 能用varchar就不用text
5. 避免将列设为NULL
6. 用索引提高查询效率
7. 可以的话,将SQL转成大写执行
8. 查看执行计划
1. 连表join代替子查询
2. 连表时类型一致
3. 使用表的别名,减少解析时间和歧义
如果就是想删除全表的记录:truncate table table_name;
使用 sql delete 语句一定要附加 WHERE 子句,如果删除所有记录,也要用 WHERE 1=1;
建索引:
1. 首先应考虑在常常用于条件判断的字段上创建索引
2. 尽量选择区分度高的列建立索引
如果为区分度低的列建立索引,会导致索引树很高
一般在2 ~ 4层
区分度公式:count(distinct col) / count(*)
数值越大表示查询的记录越少,表示字段不重复的比例
3. 当需要组合搜索时, 使用组合索引代替多个单列索引
4. 在创建联合索引时,根据业务需求,将where子句中使用最频繁的一列放在最左边, 像这种范围的放到后面去
查询时要正确命中索引:
1. select子句中尽量避免使用*
2. count(1) / count(主键/列) 代替 count(*)
3. 避免对字段使用例如:UCASE() / LCASE() / MID() 等内置函数
4. where子句比较符号左侧避免表达式、函数
尽量避免在where条件子句中,比较符号的左侧出现表达式、函数等操作
例如:
where 成绩 + 5 > 90 (表达式在比较符号的左侧)
优化方法:
where 成绩 > 90 – 5(表达式在比较符号的右侧)
5. 索引字段不要参与计算
6. 索引字段不要使用函数
7. 索引类型要一致
例:where id = 3 而不是 where id = '3'
8. 尽量避免在 where 子句中对字段进行 null 值判断
9. 对于范围查询:
查询的范围较大时,速度肯定很慢
查询的范围小,速度依然很快
特别的:
对于 != 或者 <> 来说,如果是主键,还是会走索引
对于 > 来说,如果是主键,还是会走索引,而且类型为整型,也会走索引
10. 在MySQL中,模糊查询避免开头就使用“%”
例如: LIKE '%C%';
11. 尽量避免使用in和not in
in里面的结果集如果是通过一个子查询出来的是不走索引的
in 可以用 between and
not in 可以用 not exists
12. 尽量避免使用or,使用union all代替
(如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描)
例如:elect 学号 from 成绩表 where 成绩 = 88 or 成绩 = 89
优化后:
select 学号 from 成绩表 where 成绩 = 88
union
select 学号 from 成绩表 where 成绩 = 89
13. 对于联合索引,要遵循最左前缀匹配原则
联合索引的最左前缀:
A、B、C3个字段
这个时候,可以使用的查询条件有:A、A+B、A+C、A+B+C,唯独不能使用B+C,即最左侧那个字段必须匹配到
解释一下最左前缀原则:
当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+树是按照从左到右的顺序来建立搜索树的,
比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,
如果name相同再依次比较age和sex,最后得到检索的数据;
但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,
因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。
比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,
所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了,
这个是非常重要的性质,即索引的最左匹配特性。
范围查询:
范围列可以用到索引(必须是最左前缀),但是范围列后面的列无法用到索引。
同时,索引最多用于一个范围列,因此如果查询条件中有两个范围列则无法全用到索引。
mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,
比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,
d是用不到索引的,
如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
14. 排序条件为索引,select的时候也是索引字段
特别的:
对于主键来说,还是会走索引
15. 使用limit子句限制返回的数据行数
0.先运行看看是否真的很慢,注意设置SQL_NO_CACHE
1.where条件单表查,锁定最小返回记录表。
这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,
单表每个字段分别查询,看哪个字段的区分度最高
2.explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)
3.order by limit 形式的sql语句让排序的表优先查
4.了解业务方使用场景
5.加索引时参照建索引的几大原则
6.命中索引
7.观察结果,不符合预期继续从0分析
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
id: 查询顺序的标识
select_type: 查询的类型
SIMPLE: 简单查询
PRIMARY: 最外层查询
SUBQUERY:映射的子查询
DRIVED: 子查询
UNION:联合
UNION RESULT:使用联合的结果
table: 访问的表
partitions:
type: 查询的访问方式
all < index < range < index_merge < ref_or_null < ref < eq_ref < const/system
all: 从前往后,全表扫描
select * from userinfo where uname = 'alex';
特别的:select * from userinfo where uname = 'alex' limit 1;
这句特别快,虽然explain后,type也是all,但这句是找第一个后就不会往后继续扫描了
index: 全索引扫描
range: 指定索引范围扫描
PS: between and / in / >= < <= 特别注意!=和>
index_merge: 索引合并
ref_or_null:
ref: 使用索引查询一个或多个值
eq_ref: 连表时条件on用primary key或者unique
const: 常量,最多一个匹配行
system: 仅一个匹配行
possible keys: 可能使用的索引
key: 真实使用的索引
key_len: 索引使用的长度
ref:
rows: 找到所需的行所读取的行数
filtered:
Extra:
1,开启慢查询日志
slow_query_log
2,配置时间限制,超过此时间,则记录
long_query_time
3,开启记录没有使用索引的查询的选项
show variables like '%queries%';
log_queries_not_using_indexes
4,日志文件路径
slow_query_log_file
show variables like '%query%';
set global 变量名 = 值
mysqld
可以改默认的配置文件(我目前还没找到)
不让看,例如博客园
select
*
from
userinfo
where
userinfo.uid
in
(select uid from userinfo limit 700000, 10);
select * from userinfo where uid > 700000 / max_id limit 10;
select
*
from
(select * from userinfo where uid < 700001 / min_id order by uid desc limit 10) as ReversedLastPage
order by
ReversedLastPage.uid asc;
199 - 196 = 3
select
*
from
userinfo
where
userinfo.uid
in (
select
UID.uid
from
(select AmongData.uid from (select uid from userinfo where uid > 当前页最大值 limit 每页数据 * [页码-当前页])
as AmongData order by AmongData.uid desc limit 10) as UID order by UID.uid asc)
order by userinfo.uid asc;