这一份笔记以 《MySQL必知必会》为基础,按照我的需求持续补充。php
完善中...不只限于该入门书上的知识html
因为原文是在Typora写的, 所以部分图片可能会没有上传上来, 有空后续会补上.mysql
最后修改时间: 2019年10月23日15:38:40git
MySQL 的两种发音:正则表达式
sequel
算法
['siːkw(ə)l]
数据库中的 schema : 关于数据库和表的布局及特性的信息sql
有时,schema 用做数据库的同义词。遗憾的是,schema 的含义一般在上下文中并非很清晰。
主键(primary key): 一列(或一组列), 其值能惟一区分表中每一行。shell
子句(clause) SQL语句由子句构成,有些子句是必需的,而有的是可选的。数据库
子句的例子有 SELECT 语句的 FROM 子句,
MariaDB 与 MySQL 版本替代:ubuntu
下载对应的yum仓库: https://dev.mysql.com/downloa...
# RHEL6 wget https://dev.mysql.com/get/mysql80-community-release-el6-3.noarch.rpm # RHEL7 wget https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm #### 如下以 RHEL6(CentOS) 为例 #### #### CentOS 7 一些命令不大同样, eg. service, chkconfig #### # 安装yum源 yum localinstall mysql80-community-release-el6-3.noarch.rpm # 确认是否已成功安装 yum repolist enabled | grep "mysql.*-community.*" # 因为默认指定最新的版本(MySQL 8.0), 所以须要手动指定咱们想安装的版本 (MySQL 5.7) # 查看MySQL Yum存储库中的全部子存储库及其状态 yum repolist all | grep mysql # 禁止8.0系列的子存储库 yum-config-manager --disable mysql80-community # 启用5.7系列的子存储库 yum-config-manager --enable mysql57-community # 或在安装时指定仓库 --disablerepo="*" --enablerepo="mysql57-community" # 安装MySQL # 会自动安装依赖: mysql-community-client, mysql-community-common, mysql-community-libs, mysql-community-libs-compat yum install mysql-community-server -y # 启动mysql server service mysqld start # 查看mysql server状态 service mysqld status # 设置开机自动启动 chkconfig mysqld on # 查看初始密码 # mysql server初始化时会建立帐号 'root'@'localhost', 默认密码存放在错误日志中 grep 'temporary password' /var/log/mysqld.log # 修改帐号密码 mysql -uroot -p mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码需包含大小写,数字,字符'; # 建立新帐号 mysql> GRANT ALL ON *.* TO yjx@'%' IDENTIFIED BY '复杂密码'; # 查看当前mysql编码 show variables like 'character%'; # 修改mysql server字符集 # 修改 /etc/my.cnf # 设置 # # [mysqld] # character_set_server=utf8 # # 设置完后重启mysqld # 不使用service来关闭mysqld的方法 mysqladmin -uroot -p shutdown
mysql_secure_installation
是用于设置root密码, 移除匿名用户等MySQL 5.7 请不要运行
mysql_secure_installation
, 由于安装时已经默认执行过了.
关闭默认实例
# 取消开机启动 chkconfig mysqld off # 关闭默认mysqld service mysqld stop
!!! mysql 5.7 如下没有 --initialize-insecure
和 --initialize
, 所以初始化数据库必须使用 mysql_install_db --datadir=【数据目录】
分别配置实例配置
mkdir -p /data/mysql_3307 mkdir -p /data/mysql_3308 chown -R mysql:mysql /data/mysql_33* # 一份简单的配置文件 # basedir 指的是mysqld的安装目录 cat > /data/mysql_3307/my.cnf <<\EOF [mysqld] user = mysql port = 3307 socket = /data/mysql_3307/mysqld.sock pid-file = /data/mysql_3307/mysqld.pid datadir = /data/mysql_3307/data basedir = /usr bind-address = 0.0.0.0 character_set_server = utf8 symbolic-links = 0 log_error = /data/mysql_3307/error.log slow_query_log = 1 slow_query_log_file = /data/mysql_3307/slow.log long_query_time = 2 EOF # 复制配置文件 sed "s/3307/3308/g" /data/mysql_3307/my.cnf > /data/mysql_3308/my.cnf # 初始化数据库 # --initialize-insecure 生成无密码的root帐号 # --initialize 生成带随机密码的root帐号 # 注意参数顺序, 必须先指定 --defaults-file=配置文件, 不然会报错 mysqld --defaults-file=/data/mysql_3307/my.cnf --initialize-insecure mysqld --defaults-file=/data/mysql_3308/my.cnf --initialize-insecure # 启动实例 mysqld_safe --defaults-file=/data/mysql_3307/my.cnf & mysqld_safe --defaults-file=/data/mysql_3308/my.cnf & # 关闭实例 mysqladmin -S /data/mysql_3307/mysqld.sock shutdown mysqladmin -S /data/mysql_3308/mysqld.sock shutdown # 链接实例 mysql -uroot -p -S /data/mysql_3307/mysqld.sock # # 注意, 默认只有 root@localhost, 需自行建立 root@'%' 帐号才能远程登陆 mysql -uroot -h192.168.190.100 -P 3308
使用mysqld_multi管理多实例
# 建立目录 mkdir -p /data/mysql_3307 mkdir -p /data/mysql_3308 chown -R mysql:mysql /data/mysql_33* # 初始化数据库 # 建议初始化时仍是读取配置文件来初始化好, 初始化完成后再将配置集中到 multi.cnf mysqld --defaults-file=/data/mysql_3309/my.cnf --initialize-insecure mysqld --defaults-file=/data/mysql_3310/my.cnf --initialize-insecure #mysqld -u mysql --basedir=/usr --datadir=/data/mysql_3309/data --initialize-insecure #mysqld -u mysql --basedir=/usr --datadir=/data/mysql_3310/data --initialize-insecure # 直接修改 /etc/my.cnf 或新建立一份multi配置 /data/multi.cnf # 如果新建立multi配置, 则执行mysql_multi时需指定该配置文件 cat > /etc/multi.cnf <<EOF [mysqld_multi] mysqld = /usr/bin/mysqld_safe mysqladmin = /usr/bin/mysqladmin [mysqld3309] user = mysql port = 3309 socket = /data/mysql_3309/mysqld.sock pid-file = /data/mysql_3309/mysqld.pid datadir = /data/mysql_3309/data basedir = /usr bind-address = 0.0.0.0 character_set_server = utf8 symbolic-links = 0 log_error = /data/mysql_3309/error.log slow_query_log = 1 slow_query_log_file = /data/mysql_3309/slow.log long_query_time = 2 EOF [mysqld3310] user = mysql port = 3310 socket = /data/mysql_3310/mysqld.sock pid-file = /data/mysql_3310/mysqld.pid datadir = /data/mysql_3310/data basedir = /usr bind-address = 0.0.0.0 character_set_server = utf8 symbolic-links = 0 log_error = /data/mysql_3310/error.log slow_query_log = 1 slow_query_log_file = /data/mysql_3310/slow.log long_query_time = 2 EOF EOF # 启动实例 3309 和 3310, 支持使用连字符语法或用逗号分隔 mysqld_multi --defaults-file=/data/multi.cnf start 3309,3310 # 关闭实例 mysqld_multi --defaults-file=/data/multi.cnf stop 3309-3310 # 查看实例状态 mysqld_multi --defaults-file=/data/multi.cnf report 3309-3310
!!!
mysqld_multi
不支持 !include 或 !includedir
eg. 采用 heartbeat + drbd + mysql 实现mysql高可用双机热备方案
未实践
目的:
eg. MySQL 主从复制
Master 配置文件
log_bin = /data/3306/mysql-bin server-id = 1 #expire_logs_days = 10 #max_binlog_size = 100M
必须打开 master 端的 Binary Log
Slave 配置文件
read-only = 1 log_bin = /data/3307/mysql-bin server-id = 2 # 可选, 指定中继日志的位置和命名 relay_log = /data/3307/relay.log # 容许备库将其重放的事件也记录到自身的二进制日志中 log_slave_updates = 1
1. 确认Master 开启了二进制日志
mysql> SHOW MASTER STATUS;
Tip. 锁表
FLUSH TABLE WITH READ LOCK; UNLOCK TABLES;
2. Master 建立专门用于主从复制的帐号
GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO 'rep'@'192.168.100.%' IDENTIFIED BY '123456';
复制帐号在主库上只须要REPLICATION SLAVE
权限,1.用来监控和管理复制的帐号须要REPLICATION CLIENT 权限,而且针对这两种目的使用同一个帐号更加容易(而不是为了两个目的各建立一个帐号)。
2.若是在主库上创建了帐号,而后从主库将数据克隆到备库上时,备库也就设置好了-变成主库所须要的配置。这样后续有须要能够方便的交换主备库角色。
3. 主-从 数据保持一致
# 主库导出数据快照 # 若表全都使用InnoDB引擎, 则可以使用 --single-transaction 来代替 --lock-all-tables # --master-data=1 导出sql中会包含CHANGE MASTER语句 # eg. CHANGE MASTER TO MASTER_LOG_FILE='bin.000003', MASTER_LOG_POS=25239; # --master-data=2 导出CHANGE MASTER语句,可是会被注释(仅在平时备份时导出用) mysqldump -uroot -p -S /data/3306/mysql.sock -A --master-data=1 --lock-all-tables > master.sql > master.sql # 从库导入 mysql -uroot -p -S /data/3307/mysql.sock < master.sql
此处假设主数据库已经在使用, 而从数据库是新的, 所以须要先保持两边数据一致
4. Slave 更改从库的链接参数
# 尝试在此处不设置 MASTER_LOG_FILE,MASTER_LOG_POS, 结果后面 START SLAVE 后一直出错 # 此处的 MASTER_LOG_FILE,MASTER_LOG_POS 能够在日志中查看 mysql> CHANGE MASTER TO MASTER_HOST='192.168.190.100',MASTER_PORT=3309,MASTER_USER='rep',MASTER_PASSWORD='123456',MASTER_LOG_FILE='bin.000003', MASTER_LOG_POS=25239; # 确认一下配置文件正确 cat /data/3307/data/master.info # 从库链接主库 mysql> START SLAVE; # 确认链接正常 mysql> SHOW SLAVE STATUS\G;
-- 查看master的状态, 尤为是当前的日志及位置 show master status; -- 查看slave的状态. show slave status; -- 重置slave状态,用于删除SLAVE数据库的relaylog日志文件,并从新启用新的relaylog文件.会忘记 主从关系,它删除master.info文件和relay-log.info 文件 reset slave; -- 启动slave 状态(开始监听msater的变化) start slave; -- 暂停slave状态; stop slave; -- 跳过致使复制终止的n个事件,仅在slave线程没运行的情况下使用 set global sql_slave_skip_counter = n;
如下参数中, 部分参数只对InnoDB引擎有效
参数名 | 含义 | 建议 |
---|---|---|
max_connections |
最大客户端链接数 | |
innodb_buffer_pool_size |
Innodb存储引擎缓存池大小 | 建议设置为物理内存的 80% 左右 |
innodb_file_per_table |
Innodb中每一个表存放使用独立的表空间 不然是放在共享空间 |
设为 1, 表示每一个表使用独立表空间, 方便回收空间. |
innodb_log_file_size |
事务日志(Redo Log)单个大小(文件 ib_logfile* |
总的日志大小足以容纳1个小时的量 |
innodb_log_files_in_group |
事务日志数量 | 默认是 2 |
innodb_flush_logs_at_trx_commit |
事务提交时写日志的方式 0: 每秒将日志持久化到磁盘. 数据库崩溃时丢失最多1秒数据 1: 默认, 每次事务提交都将日志持久化到磁盘, 最安全, 性能最通常. 2: 每次事务提交都写入磁盘(指磁盘缓冲区), 具体什么时候持久化到磁盘则由操做系统控制. 系统崩溃时丢失数据 |
不推荐设为 0. 对性能要求较高能够设置为 2. |
sync_binlog |
MySQL 控制写入 BinLog 的方式 0 : 每次事务提交写入磁盘缓冲区, 由操做系统控制什么时候持久化 N: 每进行N个事务提交后持久化到磁盘, 当N=1时最安全但性能最差 5.7.7及之后默认是1, 以前默认是0 |
|
mysql 自带 mysqlslap 压测工具, 能够自行测试, 我的未使用过.
调整Redo Log大小通常是经过修改 innodb_log_file_size
配置.
在业务高峰期, 计算出1分钟写入的redo log量
# 只显示结果中 Log 相关 > pager grep Log; # 查看日志位置, sleep > show engine innodb status\G; select sleep(60); show engine innodb status\G; # 取消结果过滤 > nopager;
经过查看两次的 Log sequence number
值, 计算出差值, 单位是字节.
将上述值乘以 60 获得合理的 Redo Log 大小. 所以 innodb_log_file_size
推荐设置成 估算的Redo Log 大小 / 日志文件数(innodb_log_files_in_group
)
innodb_log_file_size
值, 并将数据目录下的全部 ib_logfile*
文件move走(先不删, 防止出问题没法启动)ib_logfile*
备注: 有看到说 mysql5.6 版本及之后无需手动删除
ib_logfile*
文件.
若是Redo Log过小, 会致使频繁刷脏页??
太大会致使故障恢复时恢复时间太长(甚至长到不可接受的程度)
USE 数据库名; -- 选择数据库 SHOW DATABASES; -- 查看数据库列表 SHOW TABLES; -- 查看当前数据库内的表的列表 SHOW COLUMNS FROM `表名`; -- 查看表结构 SHOW STATUS; -- 用于显示普遍的服务器状态信息 SHOW CREATE DATABASE `数据库名`; -- 显示建立特定数据库的MySQL语句 SHOW CREATE TABLE `表名`; -- 显示建立表的MySQL语句; SHOW GRANTS; -- 显示受权指定用户的安全权限, 默认是当前用户 SHOW GRANTS FOR 用户@"..." -- eg. root@'%' 或 root@localhost 或 root@0.0.0.0 SHOW ERRORS -- 用来显示服务器错误消息 SHOW WARNINGS -- 用来显示服务器警告消息
DESCRIBE 表名
等同于 SHOW COLUMNS FROM 表名
, MySQL独有
STATUS
快速查看当前实例状态, eg.
-------------- mysql Ver 14.14 Distrib 5.7.22, for Linux (x86_64) using EditLine wrapper Connection id: 28 Current database: mysql_learn Current user: root@localhost SSL: Not in use Current pager: stdout Using outfile: '' Using delimiter: ; Server version: 5.7.22-0ubuntu18.04.1 (Ubuntu) Protocol version: 10 Connection: 127.0.0.1 via TCP/IP Insert id: 114 Server characterset: latin1 Db characterset: latin1 Client characterset: utf8 Conn. characterset: utf8 TCP port: 3306 Uptime: 7 days 23 hours 29 min 13 sec Threads: 6 Questions: 817 Slow queries: 0 Opens: 205 Flush tables: 1 Open tables: 150 Queries per second avg: 0.001 --------------
数值数据类型
<u> 有符号或无符号</u>全部数值数据类型(除 BIT 和 BOOLEAN 外)均可以有符号或无符号。有符号数值列能够存储正或负的数值,无符号数值列只能存储正数。默认状况为有符号,但若是你知道本身不须要存储负值,可使用 UNSIGNED 关键字,这样作将容许你存储两倍大小的值。
类型 | 大小 | 范围(有符号) | 范围(无符号) | 用途 |
---|---|---|---|---|
TINYINT | 1 字节 | (-128,127) | (0,255) | 小整数值 |
SMALLINT | 2 字节 | (-32 768,32 767) | (0,65 535) | 大整数值 |
MEDIUMINT | 3 字节 | (-8 388 608,8 388 607) | (0,16 777 215) | 大整数值 |
INT或INTEGER | 4 字节 | (-2 147 483 648,2 147 483 647) | (0,4 294 967 295) | 大整数值 |
BIGINT | 8 字节 | (-9,223,372,036,854,775,808,9 223 372 036 854 775 807) | (0,18 446 744 073 709 551 615) | 极大整数值 |
FLOAT | 4 字节 | (-3.402 823 466 E+38,-1.175 494 351 E-38),0,(1.175 494 351 E-38,3.402 823 466 351 E+38) | 0,(1.175 494 351 E-38,3.402 823 466 E+38) | 单精度 浮点数值 |
DOUBLE | 8 字节 | (-1.797 693 134 862 315 7 E+308,-2.225 073 858 507 201 4 E-308),0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) | 0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) | 双精度 浮点数值 |
DECIMAL | DECIMAL(M,D) ,若是M>D,为M+2不然为D+2 | 依赖于M和D的值 | 依赖于M和D的值 | 小数值 |
关键字INT是INTEGER的同义词,关键字DEC是DECIMAL的同义词。
int(m) 里的m是表示 SELECT 查询结果集中的显示宽度,并不影响实际的取值范围.
FLOAT 类型
float(m,d)
DOUBLE 类型
double(m,d)
DECIMAL 类型
- 精确值
decimal(m,d)
m<=65, d<=30, m是总位数, d是小数位数
字符串数据类型
类型 | 大小 | 用途 |
---|---|---|
CHAR | 0-255字节 | 定长字符串 |
VARCHAR | 0-65535 字节 | 变长字符串 |
TINYBLOB | 0-255字节 | 不超过 255 个字符的二进制字符串 |
TINYTEXT | 0-255字节 | 短文本字符串 |
BLOB | 0-65 535字节 | 二进制形式的长文本数据 |
TEXT | 0-65 535字节 | 长文本数据 |
MEDIUMBLOB | 0-16 777 215字节 | 二进制形式的中等长度文本数据 |
MEDIUMTEXT | 0-16 777 215字节 | 中等长度文本数据 |
LONGBLOB | 0-4 294 967 295字节 | 二进制形式的极大文本数据 |
LONGTEXT | 0-4 294 967 295字节 | 极大文本数据 |
CHAR 类型
char(n) 若存入字符数小于n,则以空格补于其后,查询之时再将空格去掉。因此 char 类型存储的字符串末尾不能有空格
- 效率比 VARCHAR 高点
VARCHAR 类型
- 长度设置, 在MySQL 5以后是按<u>字符数</u>, 而不是字节数.
varchar(20)
能够存储20个<u>字符</u>- varchar 头部会占用 1个(n<=255)或2个字节(n>255)保存字符串长度, 若值可设为 null, 则还须要一个1字节记录null, 所以保存utf8编码的字符串最多可存 (65535 - 3)/3 = 21844
- 如果utf8编码
- 效率比 TEXT 高
TEXT 类型
- 建立索引时要指定前多少个字符
- 不能有默认值
- text 头部会占用 2个字节来保存长度
日期和时间类型
类型 | 大小 (字节) | 范围 | 格式 | 用途 |
---|---|---|---|---|
DATE | 3 | 1000-01-01/9999-12-31 | YYYY-MM-DD | 日期值 |
TIME | 3 | '-838:59:59'/'838:59:59' | HH:MM:SS | 时间值或持续时间 |
YEAR | 1 | 1901/2155 | YYYY | 年份值 |
DATETIME | 8 | 1000-01-01 00:00:00/9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |
TIMESTAMP | 4 | 1970-01-01 00:00:00/2038结束时间是第 2147483647 秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038年1月19日 凌晨 03:14:07 | YYYYMMDD HHMMSS | 混合日期和时间值,时间戳 |
每一个时间类型有一个有效值范围和一个"零"值,当指定不合法的MySQL不能表示的值时使用"零"值。
TIMESTAMP类型有专有的自动更新特性
update_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
- 保存毫秒数(相似php 的
microtime(true)
)- timestamp列默认not null。没有显式指定nullable,那么default null不合法
- mysql不会给timestamp设置默认值,除非显式设置default约束或者可空null。特例:mysql会给表第一个timestamp类型的字段同时添加
default current_timestamp
和on update timestamp
- 其余状况均会引发不合法报错
- ↑ 总结: 最好手动设置 NULL 以避免出错
日期函数:
CURRENT_TIMESTAMP
, CURRENT_TIMESTAMP()
是 NOW()
的同义词SYSDATE()
获取的是函数执行时的时间, NOW()
获取的是执行语句时的<u>开始时间</u>.二进制数据类型
子句的书写顺序很重要:
SELECT ... FROM ... JOIN ... ON ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT ...
执行顺序:
FROM > WHERE(包含JOIN) > GROUP BY > 汇集函数字段计算 > HAVING > SELECT 的字段 > ORDER BY > LIMIT
举例:
SELECT player.team_id, count(*) as num -- 顺序5 FROM player JOIN team ON player.team_id = team.team_id -- 顺序1 WHERE height > 1.80 -- 顺序2 GROUP BY player.team_id -- 顺序3 HAVING num > 2 -- 顺序4 ORDER BY num DESC -- 顺序6 LIMIT 2; -- 顺序7
基本检索
SELECT `列名1`,`列名2` FROM `表名`; -- 检索指定列 SELECT * FROM `表名`; -- 检索全部列
DISTINCT关键字: 只返回不一样的值
SELECT DISTINCT `列名1` FROM `表名`; -- 只返回惟一的 `列名1`行 SELECT DISTINCT `列名1`,`列名2` FROM `表名`; -- DISTINCT应用于全部的列, 返回 (`列名1`, `列名2`) 不一样的行
LIMIT关键字: 限制结果
SELECT * FROM `表名` LIMIT <limit>; -- 限制返回最多 <limit> 条, 等同 0,<limit> SELECT * FROM `表名` LIMIT <offset>,<limit>; -- 略过前 <offset> 条记录, 返回最多 <limit> 条 SELECT * FROM `表名` LIMIT <limit> OFFSET <offset>; -- MySQL 5 语法糖
彻底限定名
SELECT `表名`.`列名` FROM `数据库名`.`表名`; -- 有一些情形须要彻底限定名
ORDER 子句
SELECT * FROM `表名` ORDER BY `列名` <dir>; -- <dir> 默认是 ASC, 可指定 DESC. SELECT * FROM `表名` ORDER BY `列名1` <dir1>,`列名2` <dir2>; -- 仅在`列名1`等值时才按`列名2`排序
排序方式可选:
- ORDER BY 字句使用的排序列不必定是显示的列, 这是容许的.
- ORDER BY
- 对文本数据排序时, 大小写的排序取决于数据库如何设置
COLLATE
操做符 | 说明 |
---|---|
= |
等于 |
<> |
不等于 |
!= |
不等于 |
< |
小于 |
<= |
小于等于 |
> |
大于 |
>= |
大于等于 |
BETWEEN ... AND ... |
在指定的两个值之间 |
... IS NULL |
没有值 |
... IS NOT NULL |
非空, 有值 |
IN (…) | 在元组 |
NOT IN (...) | 不在元组 |
AND, OR 操做符
计算次序: AND 运算符优先级更高, 在处理OR操做符前会先处理AND操做符, 举例:
SELECT prod_name,prod_price,vend_id FROM products WHERE vend_id=1002 OR vend_id=1003 AND prod_price>=10; -- 等价于 SELECT prod_name,prod_price,vend_id FROM products WHERE (vend_id=1002) OR (vend_id=1003 AND prod_price>=10);
IN操做符
SELECT * FROM `表名` WHERE `列名` IN (值1, 值2, ..., 值N);
IN
功能等同于 OR
, 那么为何用 IN
:
NOT操做符
否认它以后所跟的任何条件
... WHERE `列名` NOT IN (值1, ..., 值N); ... WHERE `列名` IS NOT NULL; ... WHERE `列名` NOT BETWEEN 1 AND 10; ... WHERE `列名` NOT EXISTS (...);
NOT 在复杂的WHERE字句中颇有用.
例如,在与 IN 操做符联合使用时, NOT 使找出与条件列表不匹配的行很是简单。MySQL支持使用 NOT 对 IN 、 BETWEEN 和
EXISTS子句取反,这与多数其余 DBMS容许使用 NOT 对各类条件
取反有很大的差异。
LIKE操做符
通配符(wildcard)
通配符 | 含义 |
---|---|
% |
任何字符出现任意次数(0,1,N), 注意不匹配 NULL |
_ |
匹配单个字符(1) |
like 匹配完整的列.
通配符置于搜索模式开始处, 不会使用索引.
注意NULL 虽然彷佛 % 通配符能够匹配任何东西,但有一个例外,即 NULL 。即便是 WHERE prod_name LIKE '%' 也不能匹配用值 NULL 做为产品名的行。
SELECT * FROM `表名` WHRER `列名` LIKE `a%d`; SELECT * FROM `表名` WHRER `列名` LIKE `a_cd`;
MySQL仅支持多数正则表达式实现的一个很小的子集。
eg. 不支持\d
,\s
等
SELECT * FROM `表名` WHERE `列名` REGEXP '^[0-9]'; -- 匹配数字开头的, 默认不区分大小写 SELECT * FROM `表名` WHERE `列名` REGEXP BINARY "正则表达式"; -- 区分大小写
多数正则表达式实现使用单个反斜杠转义特殊字符,以便能使用这些字符自己。但MySQL要求两个反斜杠(MySQL本身解释一个,正则表达式库解释另外一个)。
转义.
时, 需使用\\.
而非\.
计算字段并不实际存在于数据库表中。计算字段是运行时在 SELECT 语句内建立的。
字段(field) 基本上与列(column)的意思相同,常常互换使用,不过数据库列通常称为列,而术语字段一般用在计算字段的链接上。
AS 别名
别名(alias)是一个字段或值的替换名。
SELECT `列名` AS `别名` FROM `表名`;
别名的其余常见用途:
算数运算
圆括号可用来区分优先顺序。
函数没有SQL的可移植性强, 几乎每种主要的DBMS的实现都支持其余实现不支持的函数,并且有时差别还很大。
concat()
字符串拼接
SELECT concat(`列名1`, '(', `列名2`, ')') FROM `表名`; -- 组成: `列名1`(`列名2`)
MySQL的不一样之处 多数DBMS使用 + 或 || 来实现拼接,MySQL则使用 Concat() 函数来实现。当把SQL语句转换成MySQL语句时必定要把这个区别铭记在心
LOCATE(substr,str)
返回子串 substr 在字符串 str 中第一次出现的位置。若是子串 substr 在 str 中不存在,返回值为 0:
LOCATE(substr,str,pos)
多字节安全
返回子串 substr 在字符串 str 中的第 pos 位置后第一次出现的位置。若是 substr 不在 str 中返回 0
substr(str,pos)
pos
为1时表示截取所有
substr(str,pos,len)
concat_ws(separator, str1, str2, ...)
使用指定分隔符拼接参数, 忽略NULL
group_concat(...)
函数返回一个字符串结果,该结果由分组中的值链接组合而成。
!!! 注意 MySQL字符串位置是从1开始
MySQL 日期格式固定为: yyyy-mm-dd
, 不管是插入,更新,或WHERE字句过滤.
补充
函数 | 说明 | 示例 |
---|---|---|
from_unixtime() |
将时间戳转为日期 | from_unixtime(1559001600) |
unix_timestamp() |
将指定日期或日期字符串转换为时间戳 | unix_timestamp(Now()) |
标准误差 MySQL还支持一系列的标准误差汇集函数
AVG()
, MAX()
, MIN()
, SUM()
函数忽略列值为 NULL 的行 COUNT(*)
对表中行的数目进行计数,无论表列中包含的是空值( NULL )仍是非空值 COUNT(column)
对特定列中具备值的行进行计数,忽略NULL 值上述汇集函数可配合 DISTINCT 来使用
SELECT COUNT(DISTINCT `列名`) FROM `表名`; SELECT AVG(DISTINCT `列名`) FROM `表名`; SELECT SUM(DISTINCT `列名`) FROM `表名`;
CHAR_LENGTH(str) 返回值为字符串str 的长度,长度的单位为字符。一个多字节字符算做一个单字符。 对于一个包含五个二字节字符集, LENGTH()返回值为 10, 而CHAR_LENGTH()的返回值为5。 CONCAT(str1,str2,...) 字符串拼接 若有任何一个参数为NULL ,则返回值为 NULL。 CONCAT_WS(separator,str1,str2,...) 字符串拼接(自定义链接符) CONCAT_WS()不会忽略任何空字符串。 (然而会忽略全部的 NULL)。 CONV(N,from_base,to_base) 进制转换 例如: SELECT CONV('a',16,2); 表示将 a 由16进制转换为2进制字符串表示 FORMAT(X,D) 将数字X 的格式写为'#,###,###.##',以四舍五入的方式保留小数点后 D 位, 并将结果以字符串的形式返回。若 D 为 0, 则返回结果不带有小数点,或不含小数部分。 例如: SELECT FORMAT(12332.1,4); 结果为: '12,332.1000' INSERT(str,pos,len,newstr) 在str的指定位置插入字符串 pos:要替换位置其实位置 len:替换的长度 newstr:新字符串 特别的: 若是pos超过原字符串长度,则返回原字符串 若是len超过原字符串长度,则由新字符串彻底替换 INSTR(str,substr) 返回字符串 str 中子字符串的第一个出现位置。 LEFT(str,len) 返回字符串str 从开始的len位置的子序列字符。 LOWER(str) 变小写 UPPER(str) 变大写 LTRIM(str) 返回字符串 str ,其引导空格字符被删除。 RTRIM(str) 返回字符串 str ,结尾空格字符被删去。 SUBSTRING(str,pos,len) 获取字符串子序列 LOCATE(substr,str,pos) 获取子序列索引位置 REPEAT(str,count) 返回一个由重复的字符串str 组成的字符串,字符串str的数目等于count 。 若 count <= 0,则返回一个空字符串。 若str 或 count 为 NULL,则返回 NULL 。 REPLACE(str,from_str,to_str) 返回字符串str 以及全部被字符串to_str替代的字符串from_str 。 REVERSE(str) 返回字符串 str ,顺序和字符顺序相反。 RIGHT(str,len) 从字符串str 开始,返回从后边开始len个字符组成的子序列 SPACE(N) 返回一个由N空格组成的字符串。 SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len) 不带有len 参数的格式从字符串str返回一个子字符串,起始于位置 pos。带有len参数的格式从字符串str返回一个长度同len字符相同的子字符串,起始于位置 pos。 使用 FROM的格式为标准 SQL 语法。也可能对pos使用一个负值。倘若这样,则子字符串的位置起始于字符串结尾的pos 字符,而不是字符串的开头位置。在如下格式的函数中能够对pos 使用一个负值。 mysql> SELECT SUBSTRING('Quadratically',5); -> 'ratically' mysql> SELECT SUBSTRING('foobarbar' FROM 4); -> 'barbar' mysql> SELECT SUBSTRING('Quadratically',5,6); -> 'ratica' mysql> SELECT SUBSTRING('Sakila', -3); -> 'ila' mysql> SELECT SUBSTRING('Sakila', -5, 3); -> 'aki' mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2); -> 'ki' TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str) TRIM(remstr FROM] str) 返回字符串 str , 其中全部remstr 前缀和/或后缀都已被删除。若分类符BOTH、LEADIN或TRAILING中没有一个是给定的,则假设为BOTH 。 remstr 为可选项,在未指定状况下,可删除空格。 mysql> SELECT TRIM(' bar '); -> 'bar' mysql> SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx'); -> 'barxxx' mysql> SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx'); -> 'bar' mysql> SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz'); -> 'barx'
GROUP BY ... HAVING ...
汇集 配合 分组 GROUP BY 子句指示MySQL分组数据,而后对每一个组而不是整个结果集进行汇集。
使用 GROUP BY
的重要规定
??
HAVING
过滤的是分组
WHERE
过滤的是行
HAVING
可使用别名HAVING 和 WHERE 的差异
这里有另外一种理解方法, WHERE 在数据分组前进行过滤, HAVING 在数据分组后进行过滤。这是一个重要的区别, WHERE 排除的行不包括在分组中。这可能会改变计算值,从而影响 HAVING 子句中基于这些值过滤掉的分组。
根据子查询执行的次数, 对子查询进行分类:
查询不依赖外层(即与主查询无关), 该子查询只需执行一次, 获得的数据结果能够做为外层查询的条件直接使用.
查询时依赖外层(用到外层表数据, 与主查询相关), 所以每次外层查询都要根据外层查询结果再进行子查询, 该子查询须要执行屡次.
在实际使用时因为性能的限制,不能嵌套太多的子查询。
eg. 返回订购产品 TNT2 的客户列表
select * from customers where cust_id in (select distinct cust_id from orders where order_num in (select order_num from orderitems where prod_id="TNT2") );
存在性子查询
判断条件是否知足, 知足为True, 不然为False
集合比较子查询
判断是否在集合中
ANY
必须与比较操做符一块儿使用, 与子查询中<u>任意值</u>比较
SELECT * FROM A WHERE A.cc > ANY (SELECT cc FROM B);
是ANY的别名, 等价于 ANY, 通常经常使用 ANY
ALL
必须与比较操做符一块儿使用, 与子查询中<u>全部值</u>比较
SELECT * FROM A WHERE A.cc > ALL (SELECT cc FROM B);
Q. 对于 表 A, B 的子查询, EXISTS 和 IN 要选哪一个?
A. 须要根据表A 和 表B 的表大小及索引状况而定.
一般使用 IN 及 EXISTS 状况能够归纳为如下 SQL 语句:
-- 语句1 SELECT * FROM A WHERE cc IN (SELECT cc FROM B); -- 语句2 SELECT * FROM A WHERE EXISTS (SELECT cc FROM B WHERE A.cc = B.cc);
原则: 小表驱动大表.
结论:
这里的表大小指的是根据where筛选后的大小, 而非表的原始大小
联结是一种机制,用来在一条 SELECT语句中关联表,所以称之为联结。
使用特殊的语法,能够联结多个表返回一组输出,联结在运行时关联表中正确的行。
使用联结的要点:
<u>表别名</u>只在查询执行中使用。与列别名不同,表别名不返回到客户机。
<u>彻底限定列名</u> 在引用的列可能出现二义性时,必须使用彻底限定列名(用一个点分隔的表名和列名)。若是引用一个没有用表名限制的具备二义性的列名,MySQL将返回错误。
在联结两个表时,你实际上作的是将第一个表中的每一行与第二个表中的每一行配对。 WHERE 子句做为过滤条件,它只包含那些匹配给定条件(这里是联结条件)的行。没有WHERE 子句,第一个表中的每一个行将与第二个表中的每一个行配对,而无论它们逻辑上是否能够配在一块儿。
示例代码↓
-- 将表 vendors 与表 products 联结, 联结条件是: vendors.vend_id = products.vend_id SELECT prod_id,vend_name,prod_name,prod_price FROM vendors,products WHERE vendors.vend_id = products.vend_id; -- 表 vendors 每一行都将于表 products 每一行配对 -- 此时返回的结果为 笛卡尔积, 返回行数目是: count(表A) * count(表B) SELECT prod_id,vend_name,prod_name,prod_price FROM vendors,products;
根据获取到的结果集的范围将链接进行分类:
隐式的内链接: 不写 INNER JOIN
显式的内链接: 写 INNER JOIN
左外链接 LEFT OUTER JOIN
右外链接 RIGHT OUTER JOIN
全外链接 FULL OUTER JOIN
书写时 OUTER
能够忽略
CROSS JOIN
返回记录的条数是每一个表的行数的乘积.
根据链接时的测试条件, 将链接进行分类:
当自身与自身进行链接时, 称为<u>自链接</u>.
<u>内部联结</u>即上面的<u>等值联结</u>, 它基于两个表之间的相等测试。
SELECT vendors.vend_id,vend_name,prod_name,prod_price FROM vendors INNER JOIN products ON vendors.vend_id = products.vend_id;
<u>使用哪一种语法?</u> ANSI SQL规范首选 INNER JOIN 语法。此外,尽管使用 WHERE 子句定义联结的确比较简单,可是使用明确的联结语法可以确保不会忘记联结条件,有时候这样作也能影响性能。
<u>性能考虑</u> MySQL在运行时关联指定的每一个表以处理联结。这种处理多是很是耗费资源的,所以应该仔细,不要联结没必要要的表。联结的表越多,性能降低越厉害。
eg. 返回订购产品 TNT2 的客户列表
-- 子查询方式 select * from customers where cust_id in (select distinct cust_id from orders where order_num in (select order_num from orderitems where prod_id="TNT2") ); -- 表联结方式1 SELECT cust_name,cust_contact FROM customers,orders,orderitems WHERE customers.cust_id = orders.cust_id AND orders.order_num = orderitems.order_num AND orderitems.prod_id = 'TNT2'; -- 表联结方式2 SELECT cust_name,cust_contact FROM customers INNER JOIN orders ON customers.cust_id = orders.cust_id INNER JOIN orderitems ON orders.order_num = orderitems.order_num WHERE orderitems.prod_id = "TNT2";
Eg. 假如你发现某物品(其ID为 DTNTR )存在问题,所以想知道生产该物品的供应商生产的其余物品是否也存在这些问题。
-- 子查询方式 SELECT prod_id,prod_name FROM products WHERE vend_id = ( SELECT vend_id FROM products WHERE prod_id = 'DTNTR' ); -- 自联结方式 SELECT p1.prod_id,p1.prod_name FROM products as p1,products as p2 WHERE p1.vend_id = p2.vend_id AND p2.prod_id = 'DTNTR';
<u>用自联结而不用子查询</u> 自联结一般做为外部语句用来替代从相同表中检索数据时使用的子查询语句。虽然最终的结果是相同的,但有时候处理联结远比处理子查询快得多。应该试一下两种方法,以肯定哪种的性能更好
<u>外部联结</u>: 联结包含了那些在相关表中没有关联行的行。
许多联结将一个表中的行与另外一个表中的行相关联。但有时候会需
要包含没有关联行的那些行。例如,可能须要使用联结来完成如下工做:
- 对每一个客户下了多少订单进行计数,包括那些至今还没有下订单的
客户;- 列出全部产品以及订购数量,包括没有人订购的产品;
- 计算平均销售规模,包括那些至今还没有下订单的客户。
-- 使用 LEFT OUTER JOIN 从 FROM子句的左边表( customers 表)中选择全部行 select c.cust_id,o.order_num from customers as c left outer join orders as o on c.cust_id = o.cust_id; -- 查看全部客户的订单数量(汇集函数), 包含从没下过单的客户 SELECT c.cust_id,cust_name,count(distinct o.order_num) FROM customers as c LEFT OUTER JOIN orders as o ON c.cust_id=o.cust_id GROUP BY c.cust_id;
与内部联结关联两个表中的行不一样的是,外部联结还包括没有关联行的行。在使用 OUTER JOIN 语法时,必须使用 RIGHT 或 LEFT 关键字指定包括其全部行的表( RIGHT 指出的是 OUTER JOIN 右边的表,而 LEFT指出的是 OUTER JOIN 左边的表)。
OUTER 关键字能够省略不写.
<u>外部联结的类型</u> 存在两种基本的外部联结形式:左外部联结和右外部联结。它们之间的惟一差异是所关联的表的顺序不一样。换句话说,左外部联结可经过颠倒 FROM 或 WHERE 子句中表的顺序转换为右外部联结。所以,两种类型的外部联结可互换使用,而究竟使用哪种纯粹是根据方便而定。
MySQL也容许执行多个查询(多条 SELECT 语句),并将结果做为单个查询结果集返回。这些组合查询一般称为并(union)或复合查询(compound query)。
<u>组合查询和多个 WHERE 条件</u> 多数状况下,组合相同表的两个查询完成的工做与具备多个 WHERE 子句条件的单条查询完成的工做相同。
-- 返回查询(过滤重复行) SELECT ... FROM ... UNION SELECT ... FROM ... ORDER BY ... -- 返回查询(保留全部行) SELECT ... FROM ... UNION ALL SELECT ... FROM ... ORDER BY ...
对于更复杂的过滤条件,或者从多个表(而不是单个表)中检索数据的情形,使用 UNION 可能会使处理更简单。
UNION规则
特色
重要说明
MyISAM 引擎支持, InnoDB 引擎不支持.
为了进行全文本搜索,必须索引被搜索的列
CREATE TABLE table_name( note_id int NOT NULL AUTO_INCREMENT, note_text text NULL, PRIMARY KEY (note_id), FULLTEXT(note_text), -- 建立全文本索引 ) ENGINE=MyISAM;
<u> !!不要在导入数据时使用 FULLTEXT</u>更新索引要花时间,虽然不是不少,但毕竟要花时间。若是正在导入数据到一个新表,此时不该该启用 FULLTEXT 索引。应该首先导入全部数据,而后再修改表,定义 FULLTEXT 。这样有助于更快地导入数据(并且使索引数据的总时间小于在导入每行时分别进行索引所需的总时间)。
全文本搜索返回的结果默认排序是按照关联程度最高的排在最前面
-- 针对指定的列进行搜索 SELECT * FROM `表名` WHERE Match(`列名`) Against('搜索词');
Match(
列名) Against('搜索词')
其实是计算出一个表明关联程度的数值, 该数值能够在 SELECT 中直接查看.
?? <u>使用完整的 Match() 说明</u> 传递给 Match() 的值必须与FULLTEXT() 定义中的相同。若是指定多个列,则必须列出它们(并且次序正确)。
<u>!!搜索不区分大小写</u> 除非使用 BINARY 方式(本章中没有介绍),不然全文本搜索不区分大小写。
-- WITH QUERY EXPANSION 使用查询扩展 SELECT note_id,note_text FROM productnotes WHERE match(note_text) against('anvils' WITH QUERY EXPANSION);
MySQL对数据和索引进行两遍扫描来完成搜索:
利用查询扩展,能找出可能相关的结果,即便它们并不精确包含所查找的词。
<u>行越多越好</u> 表中的行越多(这些行中的文本就越多),使用查询扩展返回的结果越好。
布尔方式(booleanmode)
<u>即便没有 FULLTEXT 索引也可使用</u> 布尔方式不一样于迄今为止使用的全文本搜索语法的地方在于,即便没有定义
FULLTEXT 索引,也可使用它。但这是一种很是缓慢的操做(其性能将随着数据量的增长而下降)。
SELECT note_id,note_text FROM productnotes WHERE match(note_text) against('heavy -rope*' IN BOOLEAN MODE);
说明:
- 匹配
heavy
- 排除
rope
开头的词
几种使用方式
插入时必须对每一个列必须提供一个值.
-- 简单但不安全, 依赖表中列的定义次序 INSERT INTO customer VALUES(NULL,'pep', '100 main', 'los angles', 'CA', '90046', 'USA', NULL, NULL); -- 指定插入的列, 推荐(但很繁琐) INSERT INTO customers(cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country,cust_contact,cust_email) VALUES('pep', '100 main', 'los angles', 'CA', '90046', 'USA', NULL, NULL); -- 插入多行 INSERT INTO `表名`(`列名1`, `列名2`) VALUES("值1", "值2"),("值3", "值4"),("值5", "值6"); -- 插入检索出的数据, 注意避免主键的冲突 INSERT INTO `表1`(`列名1`, `列名2`) SELECT `列名1`, `列名2` FROM `表2`;
<u>插入时省略列需知足如下任一条件</u>:
<u>下降插入优先级</u> INSERT LOW PRIORITY INTO
↑ LOW PRIORITY
一样适用于 UPDATE
和 DELETE
语句
提升 INSERT 的性能 一次插入多条记录能够提升数据库处理的性能,由于MySQL用单条 INSERT 语句处理多个插入比使用多条 INSERT语句快。
<u>INSERT SELECT 中的列名</u> MySQL不关心 SELECT 返回的列名。它使用的是列的位置,所以 SELECT 中的第一列(无论其列名)将用来填充表列中指定的第一个列,第二列将用来填充表列中指定的第二个列,如此等等。这对于从使用不一样列名的表中导入数据是很是有用的。
好习惯:
UPDATE `表名` SET `列1`="值1", `列2`="值2" WHERE ...; -- IGNORE, 更新多行时, 忽略错误 UPDATE IGNORE `表名` SET ... WHERE ...;
<u>IGNORE 关键字</u> 若是用 UPDATE 语句更新多行,而且在更新这些行中的一行或多行时出一个现错误,则整个 UPDATE 操做被取消(错误发生前更新的全部行被恢复到它们原来的值)。为即便是发生错误,也继续进行更新,可以使用 IGNORE 关键字
DELETE FROM `表名` WHERE ...;
<u>删除表的内容而不是表</u> DELETE 语句从表中删除行,甚至是删除表中全部行。可是, DELETE 不删除表自己。
<u>更快的删除</u> 若是想从表中删除全部行,不要使用 DELETE 。可以使用 TRUNCATE TABLE 语句,它完成相同的工做,但速度更快( TRUNCATE 实际是删除原来的表并从新建立一个表,而不是逐行删除表中的数据)。
-- 示例 CREATE TABLE IF NOT EXISTS `user_accounts`( `id` int(100) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY, `password` varchar(64) NOT NULL COMMENT '用户密码', `reset_password` tinyint(2) NOT NULL DEFAULT 0 COMMENT '用户类型:0-不须要重置密码;1-须要重置密码', `mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机', `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- 建立惟一索引, 不容许重复 UNIQUE KEY idx_user_mobile(`mobile`) -- 索引名可忽略: UNIQUE INDEX (`mobile`) -- 建立外键 -- FOREIGN KEY (`dept_id`) REFERENCES `depts`(`id`) ON DELETE cascade -- PRIMARY KEY (`id`) -- PRIMARY KEY (`key1`,`key2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
主键值 必须惟一。表中的每一个行必须具备惟一的主键值。若是主键使用单个列,则它的值必须惟一。若是使用多个列,则这些列的组合值必须惟一。
eg. 多个列的组合做为主键
CREATE TABLE IF NOT EXISTS orderitems ( order_num int NOT NULL, order_item int NOT NULL, prod_id char(10) NOT NULL, quantity int NOT NULL, item_price decimal(8,2) NOT NULL, PRIMARY KEY(order_num, order_item) ) ENGINE=InnoDB;orderitems 表包含orders表中每一个订单的细节。每一个订单有多项物品,但每一个订单任什么时候候都只有1个第一项物品,1个第二项物品,如此等等。所以,订单号( order_num 列)和订单物品( order_item 列)的组
合是惟一的,从而适合做为主键
NULL值就是没有值或缺值。容许 NULL 值的列也容许在插入行时不给出该列的值。不容许 NULL 值的列不接受该列没有值的行,
<u>理解 NULL</u> 不要把 NULL 值与空串相混淆。 NULL 值是没有值,它不是空串。若是指定 '' (两个单引号,其间没有字符),这在 NOT NULL 列中是容许的。空串是一个有效的值,它不是无值。 NULL 值用关键字 NULL 而不是空串指定。
主键和 NULL 值 主键为其值惟一标识表中每一个行的列。<u>主键中只能使用不容许 NULL 值的列</u>。容许 NULL 值的
列不能做为惟一标识。
AUTO_INCREMENT
last_insert_id()
函数返回最后一个 AUTO_INCREMENT 值.
eg. 增长一个新订单
- orders 表中建立一行
- 使用
last_insert_id()
获取自动生成的order_num
值- 在 orderitms 表中对订购的每项物品建立一行。 order_num 在 orderitems 表中与订单细节一块儿存储。
DEFAULT
使用当前时间做为默认值
CREATE TABLE `表名`( ..., `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP );
ENGINE
<u>外键不能跨引擎</u> 混用引擎类型有一个大缺陷。外键(用于强制实施引用完整性,如第1章所述)不能跨引擎,即便用一个引擎的表不能引用具备使用不一样引擎的表的外键。
列和外键的操做
-- 新增列 ALTER TABLE `表名` ADD COLUMN `列名` 列属性; -- 删除列 ALTER TABLE `表名` DROP COLUMN `列名`; -- 修改列(属性替换) -- CHANGE 能够重命名列名, MODIFY 不能 ALTER TABLE `表名` CHANGE COLUMN `旧列名` `新列名` 列属性; ALTER TABLE `表名` MODIFY `列名` 列属性; -- 删除表 DROP TABLE `表名`; -- 重命名表 RENAME TABLE `表名1` TO `表名2`;
复杂的表结构更改通常须要手动删除过程
用新的列布局建立一个新表;
<u>当心使用 ALTER TABLE</u> 使用 ALTER TABLE 要极为当心,应该在进行改动前作一个完整的备份(模式和数据的备份)。数据库表的更改不能撤销,若是增长了不须要的列,可能不能删除它们。相似地,若是删除了不该该删除的列,可能会丢失该列中的全部数据。
-- 删除外键 -- 约束名能够用 show create table `表名` 语句来查看 ALTER TABLE `表名` DROP FOREIGN KEY `约束名`; -- 查看索引 SHOW INDEX FROM `表名`; SHOW KEY FROM `表名`; -- 建立普通索引(省略索引名) ALTER TABLE `表名` ADD INDEX (`列名`); ALTER TABLE `表名` ADD UNIQUE KEY(`列名`); ALTER TABLE `表名` ADD PRIMARY KEY(`列名`); ALTER TABLE `表名` ADD FOREIGN KEY(`列名`) REFERENCES `关联表名`(`关联列名`); ALTER TABLE `表1` ADD CONSTRAINT `约束名` FOREIGN KEY (`外键`) REFERENCES `表2` (`表2的键`); -- CREATE INDEX 只可对表增长普通索引或UNIQUE索引 CREATE INDEX `索引名` ON `表名` (`列名`); CREATE UNIQUE INDEX `索引名` ON `表名` (`列名`); -- 删除索引 ALTER TABLE `表名` DROP PRIMARY KEY; ALTER TABLE `表名` DROP INDEX `索引名`; ALTER TABLE `表名` DROP FOREIGN KEY `约束名`; DROP INDEX `索引名` ON `表名`;
2019年5月29日17:18:22 开始补充
种类:
INDEX
普通索引:仅加速查询UNIQUE KEY
惟一索引:加速查询 + 列值惟一(能够有null)PRIMARY KEY
主键索引:加速查询 + 列值惟一 + 表中只有一个(不能够有null)INDEX
组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并FULLTEXT
全文索引:对文本的内容进行分词,进行搜索<u>术语</u>
组合索引
备注: 此处将仅做为存档, 后续更新将在 这里 中处理. 2019年10月18日15:53:53
EXPLAIN 结果字段分析
select_type
查询类型 | 说明 |
---|---|
SIMPLE | 简单查询 不包含UNION查询或子查询 |
PRIMARY | 最外层查询 |
SUBQUERY | 子查询中的第一个 SELECT |
DEPENDENT SUBQUERY | !!! 子查询, 但依赖于外层查询的结果 注意确认, 避免大表驱动小表 |
DERIVED | 子查询 |
UNION | 联合 |
UNION RESULT | 使用联合的结果 |
访问的表名
匹配的分区
type
查询时的访问方式, 性能:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const
访问方式 | 说明 |
---|---|
ALL | 全表扫描,对于数据表从头至尾找一遍select * from tb1; 特别的:若是有limit限制,则找到以后就不在继续向下扫描 select * from tb1 where email = 'seven@live.com' select * from tb1 where email = 'seven@live.com' limit 1; 虽然上述两个语句都会进行全表扫描,第二句使用了limit,则找到一个后就再也不继续扫描。 |
INDEX | 全索引扫描,对索引从头至尾找一遍select nid from tb1; |
RANGE | 对索引列进行范围查找 |
INDEX_MERGE | 合并索引,使用多个单列索引搜索 |
REF | 使用索引快速定位(根据索引查找一个或多个值) |
EQ_REF | 一般出如今多表的join查询, 链接时使用primary key 或 unique 索引(都只能匹配到一行记录) |
CONST | 经过主键或惟一索引精确查找到一行 常量 表最多有一个匹配行(主键或惟一索引),由于仅有一行,在这行的列值可被优化器剩余部分认为是常数,const表很快,由于它们只读取一次 |
SYSTEM | 系统 表仅有一行(=系统表)。这是const联接类型的一个特例。 |
表示查询时,可能使用的索引
实际使用的索引
key_len
使用索引字段长度, 该字段能够评估组合索引是否彻底被使用或仅仅是最左前缀被用到.
计算规则
字符串
- char(n): n 字节长度
- varchar(n): 若是是 utf8 编码, 则是 3 n + 2字节; 若是是 utf8mb4 编码, 则是 4 n + 2 字节.
数值类型:
- TINYINT: 1字节
- SMALLINT: 2字节
- MEDIUMINT: 3字节
- INT: 4字节
- BIGINT: 8字节
时间类型
- DATE: 3字节
- TIMESTAMP: 4字节
- DATETIME: 8字节
- 字段属性: NULL 属性 占用一个字节. 若是一个字段是 NOT NULL 的, 则没有此属性.
列与索引的比较,表示上述表的链接匹配条件,即哪些列或常量被用于查找索引列上的值
估算的须要扫描的行数
Extra
该列包含MySQL解决查询的详细信息
值 | 说明 |
---|---|
Using filesort | mysql对结果排序进行额外排序. mysql有两种文件排序算法,这两种排序方式均可以在内存或者磁盘上完成 explain不会告诉你mysql将使用哪种文件排序 也不会告诉你排序会在内存里仍是磁盘上完成。 |
Using index | 使用覆盖索引,以免访问表。不要把覆盖索引和index访问类型弄混了。 |
Using index condition | 索引下推优化, 5.6新增特性 |
Using temporary | 意味着mysql在对查询结果排序时会使用一个临时表 |
Using where | 这意味着mysql服务器将在存储引擎检索行后再进行过滤 许多where条件里涉及索引中的列,当(而且若是)它读取索引时,就能被存储引擎检验 所以不是全部带where子句的查询都会显示“Using where”。 有时“Using where”的出现就是一个暗示:查询可受益于不一样的索引。 |
Range checked for each record(index map: N) | 这个意味着没有好用的索引,新的索引将在联接的每一行上从新估算,N是显示在possible_keys列中索引的位图,而且是冗余的。 |
备注: 此处将仅做为存档, 后续更新将在 这里 中处理. 2019年10月18日15:53:53
show profile 分析SQL性能工具(检测数据存在临时表中)
SET profiling=1;
show profiles
, show profile
, show profile for query <id>
视图是虚拟的表。与包含数据的表不同,视图只包含使用时动态检索数据的查询。
视图包含的是一个SQL查询, 它不包含数据!!我的理解: 视图即别名~
-- 建立视图 CREATE VIEW `视图名` AS SELECT ... -- 查看建立指定视图的语句 SHOW CREATE VIEW `视图名`; -- 删除视图 DROP VIEW `视图名`; -- 更新视图 CREATE OR REPLACE VIEW AS SELECT ...
视图的做用:
为何使用视图:
- 重用SQL语句。
- 简化复杂的SQL操做。在编写查询后,能够方便地重用它而没必要知道它的基本查询细节。
- 使用表的组成部分而不是整个表。
- 保护数据。能够给用户授予表的特定部分的访问权限而不是整个表的访问权限。
- 更改数据格式和表示。视图可返回与底层表的表示和格式不一样的数据。
视图的规则和限制
<u>建立可重用的视图</u>
建立不受特定数据限制的视图是一种好办法。例如,上面建立的视图返回生产全部产品的客户而不只仅是 生产TNT2 的客户。扩展视图的范围不只使得它能被重用,并且甚至更有用。这样作不须要建立和维护多个相似视图。
<u>将视图用于检索</u>
通常,应该将视图用于检索( SELECT 语句)而不用于更新( INSERT 、 UPDATE 和 DELETE )。
简单来讲: 存储过程是为之后的使用而保存的一条或多条MySQL语句的集合.
使用存储过程的缘由:
提升性能
使用存储过程比使用单独的SQL语句要快
存储过程通常并不显示结果, 而是把结果返回给你指定的变量.
-- 调用存储过程 CALL `过程名`() -- 更改mysql命令行客户端语句分隔符, 除了 \ 符号外,其余字符均可以做为语句分隔符. DELIMITER // -- 建立存储过程 CREATE PROCEDURE `过程名`() BEGIN END// -- 还原mysql命令行客户端语句分隔符 DELIMITER ; -- 删除存储过程 DROP PROCEDURE IF EXISTS `过程名`; -- 检查(查看)存储过程 SHOW CREATE PROCEDURE `过程名`; -- 查看存储过程的元数据(建立时间, 建立人..) SHOW PROCEDURE STATUS LIKE '过程名';
MySQL支持存储过程的参数:
<u>参数的数据类型</u>
存储过程的参数容许的数据类型与表中使用的数据类型相同。!!! 记录集不是容许的类型,所以,不能经过一个参数返回多个行和列。
一个简单的示例: 计算商品的最低、最高、平均价格
DELIMITER // CREATE PROCEDURE productpricing( OUT pl DECIMAL(8,2), OUT ph DECIMAL(8,2), OUT pm DECIMAL(8,2) ) BEGIN SELECT Min(prod_price) INTO pl FROM products; SELECT Max(prod_price) INTO ph FROM products; SELECT Avg(prod_price) INTO pm FROM products; END// DELIMITER ; CALL productpricing(@pricelow, @pricehigh, @priceaverage); SELECT @pricelow, @pricehigh, @priceaverage;
另外一个简单示例: 接受订单号并返回该订单的统计
DELIMITER // CREATE PROCEDURE ordertotal(IN onumber INT, OUT ototal DECIMAL(6,2)) BEGIN SELECT Sum(item_price*quantity) FROM orderitems WHERE order_num = onumber INTO ototal; END// DELIMITER ; CALL ordertotal(20005, @total); SELECT @total;
<u>变量(variable)</u>
内存中一个特定的位置,用来临时存储数据。变量名不区分大小写
@变量名
, 仅对当前链接有效
可使用 SET @变量=值
或 SELECT @变量:=值;
来赋值给变量
-- SET @变量=值; -- 在SELECT中, = 是比较符, 所以须要用 := SELECT @变量:=值;
全局变量
对当前mysqld实例有效
SET GLOBAL 变量名=值; SET @@变量名=值;
须要 SUPER 权限, 需从新链接后才生效.
会话变量
只对当前链接有效
-- 设置变量值 SET SESSION 变量名=值; -- LOCAL 是SESSION的同义词 SET LOCAL 变量名=值; -- 不指定 GLOBAL,SESSION 时, 默认就是 SESSION -- 此处没有 @ SET 变量名=值; -- 若存在会话变量则返回, 不然返回全局变量. SELECT @@变量名; SHOW VARIABLES LIKE '变量名';
declare
定义, 仅在当前块有效(begin...end)
条件语句
IF ... THEN ELSEIF ... THEN ELSE END IF;
循环语句
-- WHILE 循环 WHILE ... DO END WHILE; -- --------------------------------------------------- -- REPEAT 循环 REPEAT UNTIL ... END REPEAT; -- --------------------------------------------------- -- LOOP 循环 loop标记: LOOP IF ... THEN LEAVE loop标记; END IF; END LOOP; -- LOOP 示例 CREATE PROCEDURE proc_loop () BEGIN declare i int default 0; loop_label: loop select i; set i=i+1; if i>=5 then leave loop_label; end if; end loop; END
动态执行SQL语句
PREPARE 变量 FROM "..."; EXECUTE 变量 USING @p1; DEALLOCATE PREPARE 变量; -- 示例 SET @num = 20005; PREPARE stmt FROM 'SELECT * FROM orders WHERE order_num = ?'; EXECUTE stmt USING @num; DEALLOCATE PREPARE stmt;
参数只能使用 <u>用户变量</u>来传递
在存储过程当中包含:
-- Name: ordertotal -- Parameters: onumber = order number -- taxable = 0 if not taxable, 1 if taxable -- ototal = order total VARIABLES CREATE PROCEDURE ordertotal( IN onumber INT, IN taxable BOOLEAN, OUT ototal DECIMAL(8,2) ) COMMENT '获取订单总额, 可选增长营业税' BEGIN -- Declare variable for total DECLARE total DECIMAL(8,2); -- Declare tax percentage DECLARE taxrate INT DEFAULT 6; -- Get the order total SELECT sum(item_price*quantity) FROM orderitems WHERE order_num = onumber INTO total; -- Is this taxable? IF taxable THEN -- yes, so add taxrate to the total SELECT total+(total/100*taxrate) INTO total; END IF; -- And finally, save to out vaiable SELECT total INTO ototal; END;
游标(cursor)是一个存储在MySQL服务器上的数据库查询,它不是一条 SELECT 语句,而是被该语句检索出来的结果集。在存储了游标以后,应用程序能够根据须要滚动或浏览其中的数据。
<u>只能用于存储过程</u> 不像多数DBMS,MySQL游标只能用于存储过程(和函数)。
使用游标的步骤:
关闭游标
- 游标关闭后必须从新打开才能再次使用
- 存储过程结束时会自动将已打开的游标关闭
CREATE PROCEDURE `processorders`() BEGIN -- create cursor DECLARE ordernumbers CURSOR FOR SELECT order_num FROM orders; -- open cursor OPEN ordernumbers; -- close cursor CLOSE ordernumbers; END
书上示例
DROP PROCEDURE processorders; CREATE PROCEDURE processorders() BEGIN DECLARE done BOOLEAN DEFAULT 0; DECLARE o INT; DECLARE t DECIMAL(8,2); -- Declare CURSOR DECLARE ordernumbers CURSOR FOR SELECT order_num FROM orders; -- Declare continue handler -- FOR NOT FOUND DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1; -- Create a table to store the results CREATE TABLE IF NOT EXISTS ordertotals( order_num INT, total DECIMAL(8,2) ); OPEN ordernumbers; REPEAT -- Get order number FETCH ordernumbers INTO o; CALL ordertotal(o,1,t); INSERT INTO ordertotals(order_num,total) VALUES(o,t); UNTIL done END REPEAT; -- Close the cursor CLOSE ordernumbers; END
改进示例
DROP PROCEDURE processorders; CREATE PROCEDURE processorders() BEGIN DECLARE done BOOLEAN DEFAULT 0; DECLARE o INT; DECLARE t DECIMAL(8,2); -- Declare CURSOR DECLARE ordernumbers CURSOR FOR SELECT order_num FROM orders; -- Declare continue handler -- FOR NOT FOUND DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1; -- Create a table to store the results CREATE TABLE IF NOT EXISTS ordertotals( order_num INT, total DECIMAL(8,2) ); OPEN ordernumbers; FETCH ordernumbers INTO o; -- 避免插入 (NULL,NULL) 到 ordertotals 表 WHILE NOT done DO CALL ordertotal(o,1,t); SELECT o,t; INSERT INTO ordertotals(order_num,total) VALUES(o,t); FETCH ordernumbers INTO o; END WHILE; -- Close the cursor CLOSE ordernumbers; END
触发器是MySQL响应如下任意语句而自动执行的一条MySQL语句(或位于 BEGIN 和 END 语句之间的一组语
句):
建立触发器可能须要特殊的安全访问权限,可是,触发器的执行是自动的。若是 INSERT 、 UPDATE 或 DELETE 语句可以执行,则相关的触发器也能执行。
在建立触发器时,须要给出4条信息:
触发器按每一个表每一个事件每次地定义,每一个表每一个事件每次只容许一个触发器。所以,每一个表最多支持6个触发器(每条 INSERT 、 UPDATE和 DELETE 的以前和以后)。单一触发器不能与多个事件或多个表关联,因此,若是你须要一个对 INSERT 和 UPDATE 操做执行的触发器,则应该定义两个触发器。
<u>仅支持表</u> 只有表才支持触发器,视图不支持(临时表也不支持)
-- 建立触发器 CREATE TRIGGER `触发器名` AFTER|BEFORE INSERT|UPDATE|DELETE ON `表名` FOR EACH ROW ... -- 删除触发器 DROP TRIGGER `触发器名`;
<u>BEFORE 或 AFTER ?</u> 一般,将 BEFORE 用于数据验证和净化(目的是保证插入表中的数据确实是须要的数据)。本提示也适用于 UPDATE 触发器。
-- mysql中没法执行: 禁止触发器返回结果集 -- ERROR 1415 (0A000): Not allowed to return a result set from a trigger CREATE TRIGGER neworder AFTER INSERT ON orders FOR EACH ROW SELECT NEW.order_num;
在任意订单被删除前将执行此触发器。它使用一条 INSERT 语句将 OLD 中的值(要被删除的订单)保存到一个名为 archive_orders 的存档表中
CREATE TRIGGER deleteorders BEFORE DELETE ON orders FOR EACH ROW BEGIN INSERT INTO archive_orders(order_num,order_date,cust_id) VALUES(OLD.order_num,OLD.order_date,OLD.cust_id); END
使用 BEFORE DELETE 触发器的优势(相对于 AFTER DELETE 触发器来讲)为,若是因为某种缘由,订单不能存档, DELETE 自己将被放弃。
CREATE TRIGGER updatevendor BEFORE UPDATE ON vendors FOR EACH ROW SET NEW.vend_state=Upper(NEW.vend_state);
事务存在的问题:
参考: https://www.cnblogs.com/balfi...
数据库事务隔离级别:
存在全部问题, 最低的隔离级别。一个事务能够读取另外一个事务并未提交的更新结果。
解决"脏读", 大部分数据库采用的默认隔离级别。一个事务的更新操做结果只有在该事务提交以后,另外一个事务才能够的读取到同一笔数据更新后的结果。
解决"不可重复读", 整个事务过程当中,对同一笔数据的读取结果是相同的,无论其余事务是否在对共享数据进行更新,也无论更新提交与否。
解决"幻读", 最高隔离级别。全部事务操做依次顺序执行。注意这会致使并发度降低,性能最差。一般会用其余并发级别加上相应的并发锁机制来取代它。
MySQL 默认级别是 repeatable-read
术语
-- 标识事务开始 START TRANSACTION; -- ROLLBACK; -- COMMIT;
<u>哪些语句能够回退?</u> 事务处理用来管理 INSERT 、 UPDATE 和DELETE 语句。你不能回退 SELECT 语句。(这样作也没有什么意义。)你不能回退 CREATE 或 DROP 操做。事务处理块中可使用这两条语句,但若是你执行回退,它们不会被撤销。
-- 关闭本次链接的mysql自动提交 SET autocommit=0;
为了支持回退部分事务处理,必须能在事务处理块中合适的位置放置占位符。这样,若是须要回退,能够回退到某个占位符。
-- 建立保留点 SAVEPOINT `保留点名`; -- 回退到指定保留点 ROLLBACK TO `保留点名`;
术语
-- 查看可用的字符集 SHOW CHARACTER SET; SHOW CHARSET; -- 查看可用的校对 SHOW COLLATION; -- 查看当前使用的字符集 SHOW VARIABLES LIKE 'character%'; SHOW VARIABLES LIKE 'collation%';
一般系统管理在安装时定义一个默认的字符集和校对。此外,也能够在建立数据库时,指定默认的字符集和校对。
实际上,字符集不多是服务器范围(甚至数据库范围)的设置。不一样的表,甚至不一样的列均可能须要不一样的字符集,并且二者均可以在建立表时指定。
CREATE TABLE mytable( column1 INT, column2 VARCHAR(10), -- 指定特定列使用特定的字符集及校对 column3 VARCHAR(10) CHARSET latin1 COLLATE latin1_general_ci ) Engine=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- 临时区分大小写排序 SELECT * FROM mytable ORDER BY column3 COLLATE latin1_general_cs;
<u>SELECT 的其余 COLLATE 子句</u> 除了这里看到的在 ORDER BY子句 中使用之外, COLLATE 还能够用于 GROUP BY 、 HAVING 、汇集函数、别名等。
串能够在字符集之间进行转换。为此,使用 Cast() 或 Convert ()函数
用户应该对他们须要的数据具备适当的访问权,既不能多也不能少
<u>不要使用 root</u> 应该严肃对待 root 登陆的使用。仅在绝对须要时使用它(或许在你不能登陆其余管理帐号时使用)。不该该在平常的MySQL操做中使用 root 。
-- 查看当前全部用户 SELECT host,user FROM mysql.user; -- 建立用户 CREATE USER 用户名 IDENTIFIED BY '密码'; -- 重命名用户帐号 RENAME USER 旧用户名 TO 新用户名; -- 删除用户帐号 DROP USER 用户名; -- 修改用户名 RENAME USER '旧用户名@..' TO '新用户名@..'; -- 更改本身口令 SET PASSWORD = Password('密码'); -- 更改指定用户口令 SET PASSWORD FOR 用户名 = Password('密码'); ALTER USER 用户名 IDENTIFIED BY '密码';
mysqladmin
-- 设置密码(若修改密码, 则需输入原密码) mysqladmin -u root password
设置访问权限
-- 查看赋予当前用户帐号的权限 SHOW GRANTS; -- 查看赋予某个用户帐号的权限 -- 完整的用户定义: user@host -- 不指定主机名时使用默认的主机名 % -- 默认查询: 用户名@'%' SHOW GRANTS FOR 用户名; -- 建立帐号并赋予最高权限 GRANT ALL ON *.* TO '用户名'@'%' IDENTIFIED BY '密码' WITH GRANT OPTION; -- 赋予 SELECT 权限 GRANT SELECT ON `数据库名`.* TO 用户名; -- 撤销 SELECT 权限 REVOKE SELECT ON `数据库名`.* FROM 用户名;
新用户的权限为: GRANT USAGE ON *.* TO 'yjx'@'%'
, 即没有任何权限.
经常使用权限
权限 | 说明 |
---|---|
ALL | 除GRANT OPTION外的全部权限 |
SELECT | 仅查询 |
SELECT,INSERT | 查询和插入 |
USAGE | 无权限 |
目标
目标 | 说明 |
---|---|
数据库名.* | 指定数据库全部 |
数据库名.表 | 指定数据库的指定表 |
数据库名.存储过程 | 指定数据库的指定存储过程 |
. | 全部数据库 |
用户
用户 | 说明 |
---|---|
用户名@ip | 指定ip登录的用户 |
用户名@'192.168.1.%' | 指定ip段登录的用户 |
用户名@'%' | 任意ip下登录的用户 |
用户名@localhost | 本地登录的用户 |
用户名@‘192.168.200.0/255.255.255.0’ |
(子网掩码配置) |
GRANT 和 REVOKE 可在几个层次上控制访问权限:
BACKUP TABLE
或 SELECT INTO OUTFILE
语句转储数据到外部文件, 使用 RESTORE TABLE
还原mysqldump 备份
# 导出为文本文件 mysqldump -uroot -p -B '数据库名1' '数据库名2' > /tmp/mysql.bak # 直接导出为压缩文件 mysqldump -uroot -p -B '数据库名1' '数据库名2' | gzip > /tmp/mysql.bak.gz # -A, --all-databases 备份全部库 # -B, --database 备份指定库 # -F 刷新binlog日志 # -x,--lock-all-tables # -l,--locktables # --single-transaction 适合innodb事务数据库备份 # --default-character-set=utf8 字符集 # --triggers 备份触发器 # -d, --no-data 只备份表结构 # -t, --no-create-info 只备份数据, 无 create table 语句 # --master-data 增长binlog日志文件名及对应的位置点 # 生产环境全备份 # 进行数据库全备,(生产环境还经过定时任务每日凌晨执行) mysqldump -uroot -p123456 -S /data/3306/mysql.sock --single-transaction -F -B "数据库名" | gzip > /server/backup/mysql_$(date +%F).sql.gz # innodb引擎备份 mysqldump -u$MYUSER -p$MYPASS -S $MYSOCK -F --single-transaction -A | gzip > $DATA_FILE # myisam引擎备份 mysqldump -u$MYUSER -p$MYPASS -S $MYSOCK -F -A -B --lock-all-tables |gzip >$DATA_FILE
恢复
# 直接从sql文件恢复 mysql -uroot -p < /tmp/mysql.sql # 从压缩文件中恢复 gunzip < /tmp/mysql.bak.gz | mysql -uroot -p
SHOW PROCESSLIST
显示全部活动进程(以及它们的线程ID和执行时间)。你还能够用 KILL
命令终结某个特定的进程(使用这个命令须要做为管理员登陆)。在恢复数据时,可能会导入大量的数据。
此时有一些技巧能够提升导入速度:
导入时禁用索引, 导入结束后再开启索引
ALTER TABLE 表名 disable keys; ALTER TABLE 表名 enable keys;
对于InnoDB, 因为默认 autocommit=1
, 即每条语句做为单独的事务, 所以能够将多条合并成少数几条事务以加快速度.
-- 关闭自动提交, 也能够用 begin 或 start transaction set autocommit = 0; -- 插入若干条提交 insert into ...; ... insert into ...; commit; -- 插入若干条提交 insert into ...; ... insert into ...; commit; set autocommit = 1;
mysql> show open tables where in_use > 0; +----------+-------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+-------+--------+-------------+ | test_yjx | user | 1 | 0 | +----------+-------+--------+-------------+
备注: 此处将仅做为存档, 后续更新将在 这里 中处理. 2019年10月18日15:53:53
[mysqld] # ...此处省略了其余与慢查询日志不相关的配置 ############### 慢查询日志 ################ slow_query_log=1 # 打开慢查询日志 log_output=file # 日志记录位置 slow_query_log_file=/var/run/mysqld/mysqld-slow.log # 慢查询日志记录文件 long_query_time=3 # 慢查询时间阀值
也能够在mysql shell中修改相关 global 变量来开启(重启后失效)
当前会话中临时启用慢日志分析
set global slow_query_log = 1; set long_query_time = 0;
测试慢查询是否有效
mysql> select sleep(4);
此时日志或多出一条:
# Time: 190215 15:16:14 # User@Host: root[root] @ localhost [] # Query_time: 11.000205 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 0 SET timestamp=1550214974; select sleep(11);
# 分析慢日志 mysqldumpslow -a -n 50 -s c /var/run/mysqld/mysqld-slow.log # 参数说明 --verbose 版本 --debug 调试 --help 帮助 -v 版本 -d 调试模式 -s ORDER 排序方式 what to sort by (al, at, ar, c, l, r, t), 'at' is default al: average lock time ar: average rows sent at: average query time c: count l: lock time r: rows sent t: query time -r 反转顺序,默认文件倒序拍。reverse the sort order (largest last instead of first) -t NUM 显示前N条just show the top n queries -a 不要将SQL中数字转换成N,字符串转换成S。don't abstract all numbers to N and strings to 'S' -n NUM abstract numbers with at least n digits within names -g PATTERN 正则匹配;grep: only consider stmts that include this string -h HOSTNAME mysql机器名或者IP;hostname of db server for *-slow.log filename (can be wildcard), default is '*', i.e. match all -i NAME name of server instance (if using mysql.server startup script) -l 总时间中不减去锁定时间;don't subtract lock time from total time
超键 惟一标识元组(数据行)的属性集叫作超键.
好比普通表中主键 id 是超键, (id, name) 也是超键, (id, age) 也是超键, 由于均可以惟一标识元组(数据行).
候选键 最小超键, 不包含无用字段, 也称为 码.
以 超键 中的例子来说, (id, name) 由于包含无用的 name 字段, 因此显然它不是候选键. 而单独的 id 是候选键
主键 从候选键中选择一个, 也称为 主码.
外键 数据表中的字段是别的数据表的主键, 则称它为外键.
主属性 包含在任意候选键中的属性称为主属性.
全部范式(按照严格顺序排列):
关键: 表中任何属性都是原子性的, 不可再分.
解释: 字段不要是能够由其余字段组合/计算的.
须要保证表中的非主属性与候选键(码)彻底依赖 (即消除了部分依赖)
须要保证表中的非主属性与候选键(码)不存在传递依赖
一般只要求到这个级别.
消除主属性之间的部分依赖和传递依赖
这里的
- 部分依赖 也称为 部分函数依赖
- 传递依赖 也称为 传递函数依赖
一般要求: <u>3NF</u>
根据实际状况, 必须时能够新增冗余字段来提升查询效率, 须要权衡.
范式的严格程度是依次递增, 且高级别范式确定是知足低级别范式的.
该部分主要来自: https://juejin.im/post/5c2c53...
show engines
Engine | Support | Comment |
---|---|---|
InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys |
MyISAM | YES | MyISAM storage engine |
MyISAM | Innodb | |
---|---|---|
文件格式 | 数据和索引是分别存储的,数据.MYD ,索引.MYI |
数据和索引是集中存储的,.ibd |
文件可否移动 | 能,一张表就对应.frm 、MYD 、MYI 3个文件 |
否,由于关联的还有data 下的其它文件 |
记录存储顺序 | 按记录插入顺序保存 | 按主键大小有序插入 |
空间碎片(删除记录并flush table 表名 以后,表文件大小不变) |
产生。定时整理:使用命令optimize table 表名 实现 |
不产生 |
事务 | 不支持 | 支持 |
外键 | 不支持 | 支持 |
锁支持(锁是避免资源争用的一个机制,MySQL锁对用户几乎是透明的) | 表级锁定 | 行级锁定、表级锁定,锁定力度小并发能力高 |
锁扩展表级锁(
table-level lock
):lock tables <table_name1>,<table_name2>... read/write
,unlock tables <table_name1>,<table_name2>...
。其中read
是共享锁,一旦锁定任何客户端都不可读;write
是独占/写锁,只有加锁的客户端可读可写,其余客户端既不可读也不可写。锁定的是一张表或几张表。行级锁(
row-level lock
):锁定的是一行或几行记录。共享锁:select * from <table_name> where <条件> LOCK IN SHARE MODE;
,对查询的记录增长共享锁;select * from <table_name> where <条件> FOR UPDATE;
,对查询的记录增长排他锁。这里值得注意的是:innodb
的行锁,实际上是一个子范围锁,依据条件锁定部分范围,而不是就映射到具体的行上,所以还有一个学名:间隙锁。好比select * from stu where id < 20 LOCK IN SHARE MODE
会锁定id
在20
左右如下的范围,你可能没法插入id
为18
或22
的一条新纪录。
create.sql
######################################## # MySQL Crash Course # http://www.forta.com/books/0672327120/ # Example table creation scripts ######################################## ######################## # Create customers table ######################## CREATE TABLE customers ( cust_id int NOT NULL AUTO_INCREMENT, cust_name char(50) NOT NULL , cust_address char(50) NULL , cust_city char(50) NULL , cust_state char(5) NULL , cust_zip char(10) NULL , cust_country char(50) NULL , cust_contact char(50) NULL , cust_email char(255) NULL , PRIMARY KEY (cust_id) ) ENGINE=InnoDB; ######################### # Create orderitems table ######################### CREATE TABLE orderitems ( order_num int NOT NULL , order_item int NOT NULL , prod_id char(10) NOT NULL , quantity int NOT NULL , item_price decimal(8,2) NOT NULL , PRIMARY KEY (order_num, order_item) ) ENGINE=InnoDB; ##################### # Create orders table ##################### CREATE TABLE orders ( order_num int NOT NULL AUTO_INCREMENT, order_date datetime NOT NULL , cust_id int NOT NULL , PRIMARY KEY (order_num) ) ENGINE=InnoDB; ####################### # Create products table ####################### CREATE TABLE products ( prod_id char(10) NOT NULL, vend_id int NOT NULL , prod_name char(255) NOT NULL , prod_price decimal(8,2) NOT NULL , prod_desc text NULL , PRIMARY KEY(prod_id) ) ENGINE=InnoDB; ###################### # Create vendors table ###################### CREATE TABLE vendors ( vend_id int NOT NULL AUTO_INCREMENT, vend_name char(50) NOT NULL , vend_address char(50) NULL , vend_city char(50) NULL , vend_state char(5) NULL , vend_zip char(10) NULL , vend_country char(50) NULL , PRIMARY KEY (vend_id) ) ENGINE=InnoDB; ########################### # Create productnotes table ########################### CREATE TABLE productnotes ( note_id int NOT NULL AUTO_INCREMENT, prod_id char(10) NOT NULL, note_date datetime NOT NULL, note_text text NULL , PRIMARY KEY(note_id), FULLTEXT(note_text) ) ENGINE=MyISAM; ##################### # Define foreign keys ##################### ALTER TABLE orderitems ADD CONSTRAINT fk_orderitems_orders FOREIGN KEY (order_num) REFERENCES orders (order_num); ALTER TABLE orderitems ADD CONSTRAINT fk_orderitems_products FOREIGN KEY (prod_id) REFERENCES products (prod_id); ALTER TABLE orders ADD CONSTRAINT fk_orders_customers FOREIGN KEY (cust_id) REFERENCES customers (cust_id); ALTER TABLE products ADD CONSTRAINT fk_products_vendors FOREIGN KEY (vend_id) REFERENCES vendors (vend_id);
populate.sql
######################################## # MySQL Crash Course # http://www.forta.com/books/0672327120/ # Example table population scripts ######################################## ########################## # Populate customers table ########################## INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email) VALUES(10001, 'Coyote Inc.', '200 Maple Lane', 'Detroit', 'MI', '44444', 'USA', 'Y Lee', 'ylee@coyote.com'); INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact) VALUES(10002, 'Mouse House', '333 Fromage Lane', 'Columbus', 'OH', '43333', 'USA', 'Jerry Mouse'); INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email) VALUES(10003, 'Wascals', '1 Sunny Place', 'Muncie', 'IN', '42222', 'USA', 'Jim Jones', 'rabbit@wascally.com'); INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email) VALUES(10004, 'Yosemite Place', '829 Riverside Drive', 'Phoenix', 'AZ', '88888', 'USA', 'Y Sam', 'sam@yosemite.com'); INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact) VALUES(10005, 'E Fudd', '4545 53rd Street', 'Chicago', 'IL', '54545', 'USA', 'E Fudd'); ######################## # Populate vendors table ######################## INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country) VALUES(1001,'Anvils R Us','123 Main Street','Southfield','MI','48075', 'USA'); INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country) VALUES(1002,'LT Supplies','500 Park Street','Anytown','OH','44333', 'USA'); INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country) VALUES(1003,'ACME','555 High Street','Los Angeles','CA','90046', 'USA'); INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country) VALUES(1004,'Furball Inc.','1000 5th Avenue','New York','NY','11111', 'USA'); INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country) VALUES(1005,'Jet Set','42 Galaxy Road','London', NULL,'N16 6PS', 'England'); INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country) VALUES(1006,'Jouets Et Ours','1 Rue Amusement','Paris', NULL,'45678', 'France'); ######################### # Populate products table ######################### INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('ANV01', 1001, '.5 ton anvil', 5.99, '.5 ton anvil, black, complete with handy hook'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('ANV02', 1001, '1 ton anvil', 9.99, '1 ton anvil, black, complete with handy hook and carrying case'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('ANV03', 1001, '2 ton anvil', 14.99, '2 ton anvil, black, complete with handy hook and carrying case'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('OL1', 1002, 'Oil can', 8.99, 'Oil can, red'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('FU1', 1002, 'Fuses', 3.42, '1 dozen, extra long'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('SLING', 1003, 'Sling', 4.49, 'Sling, one size fits all'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('TNT1', 1003, 'TNT (1 stick)', 2.50, 'TNT, red, single stick'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('TNT2', 1003, 'TNT (5 sticks)', 10, 'TNT, red, pack of 10 sticks'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('FB', 1003, 'Bird seed', 10, 'Large bag (suitable for road runners)'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('FC', 1003, 'Carrots', 2.50, 'Carrots (rabbit hunting season only)'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('SAFE', 1003, 'Safe', 50, 'Safe with combination lock'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('DTNTR', 1003, 'Detonator', 13, 'Detonator (plunger powered), fuses not included'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('JP1000', 1005, 'JetPack 1000', 35, 'JetPack 1000, intended for single use'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('JP2000', 1005, 'JetPack 2000', 55, 'JetPack 2000, multi-use'); ####################### # Populate orders table ####################### INSERT INTO orders(order_num, order_date, cust_id) VALUES(20005, '2005-09-01', 10001); INSERT INTO orders(order_num, order_date, cust_id) VALUES(20006, '2005-09-12', 10003); INSERT INTO orders(order_num, order_date, cust_id) VALUES(20007, '2005-09-30', 10004); INSERT INTO orders(order_num, order_date, cust_id) VALUES(20008, '2005-10-03', 10005); INSERT INTO orders(order_num, order_date, cust_id) VALUES(20009, '2005-10-08', 10001); ########################### # Populate orderitems table ########################### INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20005, 1, 'ANV01', 10, 5.99); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20005, 2, 'ANV02', 3, 9.99); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20005, 3, 'TNT2', 5, 10); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20005, 4, 'FB', 1, 10); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20006, 1, 'JP2000', 1, 55); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20007, 1, 'TNT2', 100, 10); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20008, 1, 'FC', 50, 2.50); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20009, 1, 'FB', 1, 10); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20009, 2, 'OL1', 1, 8.99); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20009, 3, 'SLING', 1, 4.49); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20009, 4, 'ANV03', 1, 14.99); ############################# # Populate productnotes table ############################# INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(101, 'TNT2', '2005-08-17', 'Customer complaint: Sticks not individually wrapped, too easy to mistakenly detonate all at once. Recommend individual wrapping.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(102, 'OL1', '2005-08-18', 'Can shipped full, refills not available. Need to order new can if refill needed.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(103, 'SAFE', '2005-08-18', 'Safe is combination locked, combination not provided with safe. This is rarely a problem as safes are typically blown up or dropped by customers.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(104, 'FC', '2005-08-19', 'Quantity varies, sold by the sack load. All guaranteed to be bright and orange, and suitable for use as rabbit bait.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(105, 'TNT2', '2005-08-20', 'Included fuses are short and have been known to detonate too quickly for some customers. Longer fuses are available (item FU1) and should be recommended.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(106, 'TNT2', '2005-08-22', 'Matches not included, recommend purchase of matches or detonator (item DTNTR).' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(107, 'SAFE', '2005-08-23', 'Please note that no returns will be accepted if safe opened using explosives.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(108, 'ANV01', '2005-08-25', 'Multiple customer returns, anvils failing to drop fast enough or falling backwards on purchaser. Recommend that customer considers using heavier anvils.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(109, 'ANV03', '2005-09-01', 'Item is extremely heavy. Designed for dropping, not recommended for use with slings, ropes, pulleys, or tightropes.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(110, 'FC', '2005-09-01', 'Customer complaint: rabbit has been able to detect trap, food apparently less effective now.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(111, 'SLING', '2005-09-02', 'Shipped unassembled, requires common tools (including oversized hammer).' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(112, 'SAFE', '2005-09-02', 'Customer complaint: Circular hole in safe floor can apparently be easily cut with handsaw.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(113, 'ANV01', '2005-09-05', 'Customer complaint: Not heavy enough to generate flying stars around head of victim. If being purchased for dropping, recommend ANV02 or ANV03 instead.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(114, 'SAFE', '2005-09-07', 'Call from individual trapped in safe plummeting to the ground, suggests an escape hatch be added. Comment forwarded to vendor.' );