MySQL必知必会我的备忘录

这一份笔记以 《MySQL必知必会》为基础,按照我的需求持续补充。php

完善中...不只限于该入门书上的知识html

因为原文是在Typora写的, 所以部分图片可能会没有上传上来, 有空后续会补上.mysql

最后修改时间: 2019年10月23日15:38:40git

基础概念

MySQL 的两种发音:正则表达式

  • My-S-Q-L
  • sequel算法

    ['siːkw(ə)l]

数据库中的 schema : 关于数据库和表的布局及特性的信息sql

有时,schema 用做数据库的同义词。遗憾的是,schema 的含义一般在上下文中并非很清晰。

主键(primary key): 一列(或一组列), 其值能惟一区分表中每一行。shell

  • 任意两行都不具备相同的主键值
  • 每一个行都必须具备一个主键值(不容许为NULL值)
  • 使用多个列做为主键值, 多个列值的组合必须是惟一(但单个列的值能够不惟一)

子句(clause) SQL语句由子句构成,有些子句是必需的,而有的是可选的。数据库

  • 一个子句一般由一个关键字和所提供的数据组成。
子句的例子有 SELECT 语句的 FROM 子句,

MariaDB 与 MySQL 版本替代:ubuntu

  • MySQL 5.1 -> MariaDB 5.1, 5.2, 5.3
  • MySQL 5.5 -> MariaDB 5.5, 10.0
  • MySQL 5.6 -> MariaDB 10.0
  • MySQL 5.7 -> MariaDB 10.2

安装

CentOS 使用yum源安装

下载对应的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 主从复制

简单原理

img

  1. Master的IO线程将操做记录到二进制日志(Binary log)中
  2. Slave的IO线程会同步拉取Master的二进制日志并写入本地中继日志(Relay log)
  3. Slave的SQL线程会从中继日志中读取操做并在当前实例上重放操做.

配置及操做

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 大小

调整Redo Log大小通常是经过修改 innodb_log_file_size 配置.

  1. 在业务高峰期, 计算出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 值, 计算出差值, 单位是字节.

  2. 评估1个小时的 redo log 量

    将上述值乘以 60 获得合理的 Redo Log 大小. 所以 innodb_log_file_size 推荐设置成 估算的Redo Log 大小 / 日志文件数(innodb_log_files_in_group)

  3. 正常关闭 mysql
  4. 修改配置中 innodb_log_file_size 值, 并将数据目录下的全部 ib_logfile* 文件move走(先不删, 防止出问题没法启动)
  5. 启动 mysql, 确认没问题后就能够删除 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_timestampon update timestamp
  • 其余状况均会引发不合法报错
  • ↑ 总结: 最好手动设置 NULL 以避免出错

日期函数:

  • CURRENT_TIMESTAMP, CURRENT_TIMESTAMP()NOW() 的同义词
  • SYSDATE() 获取的是函数执行时的时间, NOW()获取的是执行语句时的<u>开始时间</u>.

二进制数据类型

1559035285382

检索数据

SELECT 语句

子句的书写顺序很重要:

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`排序

排序方式可选:

  • ASC 升序(默认), ASCENDING
  • DESC 降序
  • ORDER BY 字句使用的排序列不必定是显示的列, 这是容许的.
  • ORDER BY
  • 对文本数据排序时, 大小写的排序取决于数据库如何设置 COLLATE

WHERE 子句

操做符 说明
= 等于
<> 不等于
!= 不等于
< 小于
<= 小于等于
> 大于
>= 大于等于
BETWEEN ... AND ... 在指定的两个值之间
... IS NULL 没有值
... IS NOT NULL 非空, 有值
IN (…) 在元组
NOT IN (...) 不在元组
  • 一个列不包含值时, 称其为空值NULL

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:

  • 语法清晰直观
  • 执行更快
  • <u>能够包含其余SELECT 语句, 得可以更动态地创建 WHERE 子句</u>

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`;

REGEXP 正则表达式

MySQL仅支持多数正则表达式实现的一个很小的子集。

eg. 不支持 \d, \s
SELECT * FROM `表名` WHERE `列名` REGEXP '^[0-9]';    -- 匹配数字开头的, 默认不区分大小写

SELECT * FROM `表名` WHERE `列名` REGEXP BINARY "正则表达式";    -- 区分大小写

多数正则表达式实现使用单个反斜杠转义特殊字符,以便能使用这些字符自己。但MySQL要求两个反斜杠(MySQL本身解释一个,正则表达式库解释另外一个)。

转义 . 时, 需使用 \\. 而非 \.

1558600943297

1558600935224

1558600968226

1558601019873

计算字段

计算字段并不实际存在于数据库表中。计算字段是运行时在 SELECT 语句内建立的。
字段(field) 基本上与列(column)的意思相同,常常互换使用,不过数据库列通常称为列,而术语字段一般用在计算字段的链接上。

AS 别名

别名(alias)是一个字段或值的替换名。

SELECT `列名` AS `别名` FROM `表名`;

别名的其余常见用途:

  • 在实际的表列名包含不符合规定的字符(如空格)时从新命名它
  • 在原来的名字含混或容易误解时扩充它,等等。

算数运算

1558601948352

圆括号可用来区分优先顺序。

函数

函数没有SQL的可移植性强, 几乎每种主要的DBMS的实现都支持其余实现不支持的函数,并且有时差别还很大。

字符串处理

concat() 字符串拼接

SELECT concat(`列名1`, '(', `列名2`, ')') FROM `表名`;    -- 组成: `列名1`(`列名2`)
MySQL的不一样之处 多数DBMS使用 + 或 || 来实现拼接,MySQL则使用 Concat() 函数来实现。当把SQL语句转换成MySQL语句时必定要把这个区别铭记在心

1558602282966

  • 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开始

日期和时间

1558603084142

MySQL 日期格式固定为: yyyy-mm-dd, 不管是插入,更新,或WHERE字句过滤.

补充

函数 说明 示例
from_unixtime() 将时间戳转为日期 from_unixtime(1559001600)
unix_timestamp() 将指定日期或日期字符串转换为时间戳 unix_timestamp(Now())

数值处理

1558603490365

汇集函数

1558603562124

标准误差 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重要规定

  • ??? GROUP BY 子句能够包含任意数目的列。这使得能对分组进行嵌套,为数据分组提供更细致的控制。
  • ??? 若是在 GROUP BY 子句中嵌套了分组,数据将在最后规定的分组上进行汇总。换句话说,在创建分组时,指定的全部列都一块儿计算(因此不能从个别的列取回数据)。
  • <u>GROUP BY 子句中列出的每一个列都必须是检索列或有效的表达式(但不能是汇集函数)。若是在 SELECT 中使用表达式,则必须在GROUP BY 子句中指定相同的表达式。不能使用别名。</u>
  • 除汇集计算语句外, SELECT 语句中的每一个列都必须在 GROUP BY 子句中给出。
  • 若是分组列中具备 NULL 值,则 NULL 将做为一个分组返回。若是列中有多行 NULL 值,它们将分为一组。
  • GROUP BY 子句必须出如今 WHERE 子句以后, ORDER BY 子句以前。

1558604730593

??

过滤分组

HAVING 过滤的是分组

WHERE 过滤的是行
  • HAVING 可使用别名

HAVING 和 WHERE 的差异

这里有另外一种理解方法, WHERE 在数据分组前进行过滤, HAVING 在数据分组后进行过滤。这是一个重要的区别, WHERE 排除的行不包括在分组中。这可能会改变计算值,从而影响 HAVING 子句中基于这些值过滤掉的分组。

子查询

根据子查询执行的次数, 对子查询进行分类:

  1. 非关联子查询

    查询不依赖外层(即与主查询无关), 该子查询只需执行一次, 获得的数据结果能够做为外层查询的条件直接使用.

  2. 关联子查询

    查询时依赖外层(用到外层表数据, 与主查询相关), 所以每次外层查询都要根据外层查询结果再进行子查询, 该子查询须要执行屡次.

在实际使用时因为性能的限制,不能嵌套太多的子查询。

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")
                 );

子查询的关键字

存在性子查询

  • EXISTS

    判断条件是否知足, 知足为True, 不然为False

集合比较子查询

  • IN

    判断是否在集合中

  • ANY

    必须与比较操做符一块儿使用, 与子查询中<u>任意值</u>比较

    SELECT * FROM A WHERE A.cc > ANY (SELECT cc FROM B);
  • SOME

    是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);

原则: 小表驱动大表.

结论:

  • 若是 B表 比较小, 且 A表在 cc 列上有索引, 则推荐使用语句1.
这里的表大小指的是根据where筛选后的大小, 而非表的原始大小
  • 若是 A表 比较小, 且 B表在 cc 列上有索引, 则推荐使用语句2.

表联结/链接 Join

1558670169544

联结是一种机制,用来在一条 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;

根据获取到的结果集的范围将链接进行分类:

  • <u>内链接</u>: 取多个表之间知足条件的数据行(交集)

    隐式的内链接: 不写 INNER JOIN

    显式的内链接: 写 INNER JOIN

  • <u>外链接</u>: 除了取知足条件的交集外, 还会取某一方不知足条件的记录.

    左外链接 LEFT OUTER JOIN

    右外链接 RIGHT OUTER JOIN

    全外链接 FULL OUTER JOIN

    书写时 OUTER 能够忽略

  • <u>交叉链接</u>: 笛卡尔积(cartesian product)(全部集合的全部组合).

    CROSS JOIN

    返回记录的条数是每一个表的行数的乘积.

根据链接时的测试条件, 将链接进行分类:

  • <u>等值链接</u>: 链接条件是等号
  • <u>非等值链接</u>: 链接条件是非等号

当自身与自身进行链接时, 称为<u>自链接</u>.

内部联结 INNER JOIN

<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> 自联结一般做为外部语句用来替代从相同表中检索数据时使用的子查询语句。虽然最终的结果是相同的,但有时候处理联结远比处理子查询快得多。应该试一下两种方法,以肯定哪种的性能更好

外部联结 OUTER JOIN

<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 语法时,必须使用 RIGHTLEFT 关键字指定包括其全部行的表( RIGHT 指出的是 OUTER JOIN 右边的表,而 LEFT指出的是 OUTER JOIN 左边的表)。

  • LEFT OUTER JOIN 左外联结, 包含坐标的所有记录, 若无对应的右边记录, 则其值为 NULL
  • RIGHT OUTER JOIN
OUTER 关键字能够省略不写.

<u>外部联结的类型</u> 存在两种基本的外部联结形式:左外部联结和右外部联结。它们之间的惟一差异是所关联的表的顺序不一样。换句话说,左外部联结可经过颠倒 FROM 或 WHERE 子句中表的顺序转换为右外部联结。所以,两种类型的外部联结可互换使用,而究竟使用哪种纯粹是根据方便而定。

组合查询 UNION

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规则

  • UNION 必须由两条或两条以上的 SELECT 语句组成,语句之间用关键字 UNION 分隔(所以,若是组合4条 SELECT 语句,将要使用3个UNION 关键字)。
  • UNION 中的每一个查询必须包含相同的列、表达式或汇集函数(不过各个列不须要以相同的次序列出)。
  • 列数据类型必须兼容:类型没必要彻底相同,但必须是DBMS能够隐含地转换的类型(例如,不一样的数值类型或不一样的日期类型)。
  • 组合查询能够引用于不一样的表

特色

  • UNION 默认会去除重复的行, 若是要返回全部匹配行, 则要使用 UNION ALL
  • UNION查询只能使用一条ORDER BY 子句, 只能出如今最后一条 SELECT 语句以后.

全文本搜索

重要说明

  • 在索引全文本数据时,短词被忽略且从索引中排除。短词定义为那些具备3个或3个如下字符的词(若是须要,这个数目能够更改)。
  • MySQL带有一个内建的非用词(stopword)列表,这些词在索引全文本数据时老是被忽略。若是须要,能够覆盖这个列表(请参阅MySQL文档以了解如何完成此工做)。
  • 许多词出现的频率很高,搜索它们没有用处(返回太多的结果)。所以,MySQL规定了一条50%规则,若是一个词出如今50%以上的行中,则将它做为一个非用词忽略。50%规则不用于 IN BOOLEANMODE 。
  • 若是表中的行数少于3行,则全文本搜索不返回结果(由于每一个词或者不出现,或者至少出如今50%的行中)。
  • 忽略词中的单引号。例如, don't 索引为 dont 。
  • 不具备词分隔符(包括日语和汉语)的语言不能恰当地返回全文本搜索结果。
  • 如前所述,仅在 MyISAM 数据库引擎中支持全文本搜索。

启用全文本搜索支持 FULLTEXT

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 。这样有助于更快地导入数据(并且使索引数据的总时间小于在导入每行时分别进行索引所需的总时间)。

进行全文本搜索 MATCH…AGAINST…

全文本搜索返回的结果默认排序是按照关联程度最高的排在最前面

-- 针对指定的列进行搜索
SELECT * FROM `表名` WHERE Match(`列名`) Against('搜索词');
Match(列名 ) Against('搜索词') 其实是计算出一个表明关联程度的数值, 该数值能够在 SELECT 中直接查看.
?? <u>使用完整的 Match() 说明</u> 传递给 Match() 的值必须与FULLTEXT() 定义中的相同。若是指定多个列,则必须列出它们(并且次序正确)。
<u>!!搜索不区分大小写</u> 除非使用 BINARY 方式(本章中没有介绍),不然全文本搜索不区分大小写。

查询扩展 WITH EXPANSION

-- WITH QUERY EXPANSION 使用查询扩展
SELECT note_id,note_text 
FROM productnotes 
WHERE match(note_text) against('anvils' WITH QUERY EXPANSION);

MySQL对数据和索引进行两遍扫描来完成搜索:

  • 首先,进行一个基本的全文本搜索,找出与搜索条件匹配的全部行;
  • 其次,MySQL检查这些匹配行并选择全部有用的词
  • 再其次,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 开头的词

1558685103097

插入数据 INSERT INTO

几种使用方式

  • 插入完整的行;
  • 插入行的一部分;
  • 插入多行;
  • 插入某些查询的结果

插入时必须对每一个列必须提供一个值.

-- 简单但不安全, 依赖表中列的定义次序
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>:

  • 该列定义为容许 NULL 值(无值或空值)。
  • 在表定义中给出默认值。这表示若是不给出值,将使用默认值。

<u>下降插入优先级</u> INSERT LOW PRIORITY INTO

LOW PRIORITY 一样适用于 UPDATEDELETE 语句

提升 INSERT 的性能 一次插入多条记录能够提升数据库处理的性能,由于MySQL用单条 INSERT 语句处理多个插入比使用多条 INSERT语句快。

<u>INSERT SELECT 中的列名</u> MySQL不关心 SELECT 返回的列名。它使用的是列的位置,所以 SELECT 中的第一列(无论其列名)将用来填充表列中指定的第一个列,第二列将用来填充表列中指定的第二个列,如此等等。这对于从使用不一样列名的表中导入数据是很是有用的。

更新和删除数据

好习惯:

  • 除非确实打算更新和删除每一行,不然绝对不要使用不带 WHERE子句的 UPDATE 或 DELETE 语句。
  • 在对 UPDATE 或 DELETE 语句使用 WHERE 子句前,应该先用 SELECT 进行测试,保证它过滤的是正确的记录,以防编写的 WHERE 子句不正确。
  • 使用强制实施引用完整性的数据库,这样MySQL将不容许删除具备与其余表相关联的数据的行。

更新数据 UPDATE

UPDATE `表名`
SET `列1`="值1", `列2`="值2"
WHERE ...;

-- IGNORE, 更新多行时, 忽略错误
UPDATE IGNORE `表名`
SET ...
WHERE ...;

<u>IGNORE 关键字</u> 若是用 UPDATE 语句更新多行,而且在更新这些行中的一行或多行时出一个现错误,则整个 UPDATE 操做被取消(错误发生前更新的全部行被恢复到它们原来的值)。为即便是发生错误,也继续进行更新,可以使用 IGNORE 关键字

删除数据 DELETE

DELETE FROM `表名`
WHERE ...;

<u>删除表的内容而不是表</u> DELETE 语句从表中删除行,甚至是删除表中全部行。可是, DELETE 不删除表自己。

<u>更快的删除</u> 若是想从表中删除全部行,不要使用 DELETE 。可以使用 TRUNCATE TABLE 语句,它完成相同的工做,但速度更快( TRUNCATE 实际是删除原来的表并从新建立一个表,而不是逐行删除表中的数据)。

建立和操纵表

建立表 CREATE TABLE

-- 示例
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

  • 每一个表只容许一个 AUTO_INCREMENT 列,并且它必须被索引(如,经过使它成为主键)
  • 在 INSERT 语句中指定一个值,只要它是惟一的(至今还没有使用过)便可,该值将被用来替代自动生成的值。后续的增量将开始使用该手工插入的值。
  • last_insert_id() 函数返回最后一个 AUTO_INCREMENT 值.

    eg. 增长一个新订单

    1. orders 表中建立一行
    2. 使用 last_insert_id() 获取自动生成的 order_num
    3. 在 orderitms 表中对订购的每项物品建立一行。 order_num 在 orderitems 表中与订单细节一块儿存储。

DEFAULT

  • 使用当前时间做为默认值

    CREATE TABLE `表名`(
        ...,
        `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
    );
  • 许多数据库开发人员使用默认值而不是 NULL 列,特别是对用于计算或数据分组的列更是如此。

ENGINE

  • InnoDB 是一个可靠的事务处理引擎,它不支持全文本搜索;
  • MEMORY 在功能等同于 MyISAM ,但因为数据存储在内存(不是磁盘)中,速度很快(特别适合于临时表);
  • MyISAM 是一个性能极高的引擎,它支持全文本搜索(参见第18章),但不支持事务处理。

<u>外键不能跨引擎</u> 混用引擎类型有一个大缺陷。外键(用于强制实施引用完整性,如第1章所述)不能跨引擎,即便用一个引擎的表不能引用具备使用不一样引擎的表的外键。

更新表 ALTER TABLE

列和外键的操做

-- 新增列
ALTER TABLE `表名` ADD COLUMN `列名` 列属性;

-- 删除列
ALTER TABLE `表名` DROP COLUMN `列名`;

-- 修改列(属性替换)
-- CHANGE 能够重命名列名, MODIFY 不能
ALTER TABLE `表名` CHANGE COLUMN `旧列名` `新列名` 列属性;
ALTER TABLE `表名` MODIFY `列名` 列属性;
    

-- 删除表
DROP TABLE `表名`;

-- 重命名表
RENAME TABLE `表名1` TO `表名2`;

复杂的表结构更改通常须要手动删除过程

用新的列布局建立一个新表;

  • 使用 INSERT SELECT 语句从旧表复制数据到新表。若是有必要,可以使用转换函数和计算字段;
  • 检验包含所需数据的新表;
  • 重命名旧表(若是肯定,能够删除它);
  • 用旧表原来的名字重命名新表;
  • 根据须要,从新建立触发器、存储过程、索引和外键。

<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>

  • 索引合并:使用多个单列索引组合查询搜索
  • 覆盖索引:select的数据列只用从索引中就可以取得,没必要读取数据行,换句话说查询列要被所建的索引覆盖

组合索引

  • 同时搜索多个条件时,组合索引的性能效率好过于多个单一索引合并

SQL Explain

备注: 此处将仅做为存档, 后续更新将在 这里 中处理. 2019年10月18日15:53:53

1559124811057

EXPLAIN 结果字段分析

  • select_type

    查询类型 说明
    SIMPLE 简单查询
    不包含UNION查询或子查询
    PRIMARY 最外层查询
    SUBQUERY 子查询中的第一个 SELECT
    DEPENDENT SUBQUERY !!! 子查询, 但依赖于外层查询的结果
    注意确认, 避免大表驱动小表
    DERIVED 子查询
    UNION 联合
    UNION RESULT 使用联合的结果
  • table

    访问的表名

  • partitions

    匹配的分区

  • 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联接类型的一个特例。
  • possible_keys

    表示查询时,可能使用的索引

  • key

    实际使用的索引

  • 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 的, 则没有此属性.
  • ref

    列与索引的比较,表示上述表的链接匹配条件,即哪些列或常量被用于查找索引列上的值

  • row

    估算的须要扫描的行数

  • filtered
  • 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列中索引的位图,而且是冗余的。

Profile

备注: 此处将仅做为存档, 后续更新将在 这里 中处理. 2019年10月18日15:53:53

show profile 分析SQL性能工具(检测数据存在临时表中)

  • 开启profile SET profiling=1;
  • 发送sql
  • 查看profile的资源开销结果 show profiles, show profile, show profile for query <id>
  • 关闭profile

视图

视图是虚拟的表。与包含数据的表不同,视图只包含使用时动态检索数据的查询。

视图包含的是一个SQL查询, 它不包含数据!!

我的理解: 视图即别名~

-- 建立视图
CREATE VIEW `视图名` AS SELECT ...

-- 查看建立指定视图的语句
SHOW CREATE VIEW `视图名`;

-- 删除视图
DROP VIEW `视图名`;

-- 更新视图
CREATE OR REPLACE VIEW AS SELECT ...

视图的做用:

  • 简化数据处理
  • 从新格式化基础数据
  • 保护基础数据

为何使用视图:

  • 重用SQL语句。
  • 简化复杂的SQL操做。在编写查询后,能够方便地重用它而没必要知道它的基本查询细节。
  • 使用表的组成部分而不是整个表。
  • 保护数据。能够给用户授予表的特定部分的访问权限而不是整个表的访问权限。
  • 更改数据格式和表示。视图可返回与底层表的表示和格式不一样的数据。

视图的规则和限制

  • 与表同样,视图必须惟一命名(不能给视图取与别的视图或表相同的名字)。
  • 对于能够建立的视图数目没有限制。
  • 为了建立视图,必须具备足够的访问权限。这些限制一般由数据库管理人员授予。
  • 视图能够嵌套,便可以利用从其余视图中检索数据的查询来构造一个视图。
  • ORDER BY 能够用在视图中,但若是从该视图检索数据 SELECT 中也含有 ORDER BY ,那么该视图中的 ORDER BY 将被覆盖。
  • 视图不能索引,也不能有关联的触发器或默认值。
  • 视图能够和表一块儿使用。例如,编写一条联结表和视图的 SELECT语句。

<u>建立可重用的视图</u>

建立不受特定数据限制的视图是一种好办法。例如,上面建立的视图返回生产全部产品的客户而不只仅是 生产TNT2 的客户。扩展视图的范围不只使得它能被重用,并且甚至更有用。这样作不须要建立和维护多个相似视图。

<u>将视图用于检索</u>

通常,应该将视图用于检索( SELECT 语句)而不用于更新( INSERT 、 UPDATE 和 DELETE )。

存储过程

存储过程

简单来讲: 存储过程是为之后的使用而保存的一条或多条MySQL语句的集合.

使用存储过程的缘由:

  • 封装复杂操做, 统一调用
  • 提升性能

    使用存储过程比使用单独的SQL语句要快
  • 存在一些只能用在单个请求中的MySQL元素和特性,存储过程可使用它们来编写功能更强更灵活的代码

存储过程通常并不显示结果, 而是把结果返回给你指定的变量.

-- 调用存储过程
CALL `过程名`()

-- 更改mysql命令行客户端语句分隔符, 除了 \ 符号外,其余字符均可以做为语句分隔符.
DELIMITER //

-- 建立存储过程
CREATE PROCEDURE `过程名`()
BEGIN

END//

-- 还原mysql命令行客户端语句分隔符
DELIMITER ;

-- 删除存储过程
DROP PROCEDURE IF EXISTS `过程名`;

-- 检查(查看)存储过程
SHOW CREATE PROCEDURE `过程名`;

-- 查看存储过程的元数据(建立时间, 建立人..)
SHOW PROCEDURE STATUS LIKE '过程名';

MySQL支持存储过程的参数:

  • IN
  • OUT
  • INOUT

<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;
  • DECLARE 定义了两个局部变量, 支持可选的默认值

游标

游标(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 语句之间的一组语
句):

  • DELETE ;
  • INSERT ;
  • UPDATE
建立触发器可能须要特殊的安全访问权限,可是,触发器的执行是自动的。若是 INSERT 、 UPDATE 或 DELETE 语句可以执行,则相关的触发器也能执行。
  • 应该用触发器来保证数据的一致性(大小写、格式等)。在触发器中执行这种类型的处理的优势是它老是进行这种处理,并且是透明地进行,与客户机应用无关。
  • 触发器的一种很是有意义的使用是建立审计跟踪。使用触发器,把更改(若是须要,甚至还有以前和以后的状态)记录到另外一个表很是容易。
  • 遗憾的是,MySQL触发器中不支持 CALL 语句。这表示不能从触发器内调用存储过程。所需的存储过程代码须要复制到触发器内。

建立触发器

在建立触发器时,须要给出4条信息:

  • 惟一的触发器名;
  • 触发器关联的表;
  • 触发器应该响应的活动( DELETE 、 INSERT 或 UPDATE );
  • 触发器什么时候执行(处理以前或以后)。

触发器按每一个表每一个事件每次地定义,每一个表每一个事件每次只容许一个触发器。所以,每一个表最多支持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 触发器。

INSERT 触发器

  • 在 INSERT 触发器代码内,可引用一个名为 NEW 的虚拟表,访问被插入的行;
  • 在 BEFORE INSERT 触发器中, NEW 中的值也能够被更新(容许更改被插入的值);
  • 对于 AUTO_INCREMENT 列, NEW 在 INSERT 执行以前包含 0 ,在 INSERT执行以后包含新的自动生成值。
-- 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;

DELETE 触发器

  • 在 DELETE 触发器代码内,你能够引用一个名为 OLD 的虚拟表,访问被删除的行;
  • OLD 中的值全都是只读的,不能更新。

在任意订单被删除前将执行此触发器。它使用一条 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 自己将被放弃。

UPDATE 触发器

  • 在 UPDATE 触发器代码中,你能够引用一个名为 OLD 的虚拟表访问之前( UPDATE 语句前)的值,引用一个名为 NEW 的虚拟表访问新更新的值;
  • 在 BEFORE UPDATE 触发器中, NEW 中的值可能也被更新(容许更改将要用于 UPDATE 语句中的值);
  • OLD 中的值全都是只读的,不能更新。
CREATE TRIGGER updatevendor
BEFORE UPDATE ON vendors 
FOR EACH ROW 
SET NEW.vend_state=Upper(NEW.vend_state);

事务

事务存在的问题:

  • 脏读
  • 不可重复读
  • 幻读

参考: https://www.cnblogs.com/balfi...

数据库事务隔离级别:

  1. read-uncommited 读未提交

    存在全部问题, 最低的隔离级别。一个事务能够读取另外一个事务并未提交的更新结果。

  2. read-commited 读已提交

    解决"脏读", 大部分数据库采用的默认隔离级别。一个事务的更新操做结果只有在该事务提交以后,另外一个事务才能够的读取到同一笔数据更新后的结果。

  3. repeatable-read 可重复读

    解决"不可重复读", 整个事务过程当中,对同一笔数据的读取结果是相同的,无论其余事务是否在对共享数据进行更新,也无论更新提交与否。

  4. serializable 序列化

    解决"幻读", 最高隔离级别。全部事务操做依次顺序执行。注意这会致使并发度降低,性能最差。一般会用其余并发级别加上相应的并发锁机制来取代它。

  • 不可重复读
  • 可重复读
  • 幻读

MySQL 默认级别是 repeatable-read


术语

  • 事务( transaction )指一组SQL语句;
  • 回退( rollback )指撤销指定SQL语句的过程;
  • 提交( commit )指将未存储的SQL语句结果写入数据库表;
  • 保留点( savepoint )指事务处理中设置的临时占位符(place-holder),你能够对它发布回退(与回退整个事务处理不一样)。
-- 标识事务开始
START TRANSACTION;

-- ROLLBACK;
-- COMMIT;

<u>哪些语句能够回退?</u> 事务处理用来管理 INSERT 、 UPDATE 和DELETE 语句。你不能回退 SELECT 语句。(这样作也没有什么意义。)你不能回退 CREATE 或 DROP 操做。事务处理块中可使用这两条语句,但若是你执行回退,它们不会被撤销。


-- 关闭本次链接的mysql自动提交
SET autocommit=0;

保留点 SavePoint

为了支持回退部分事务处理,必须能在事务处理块中合适的位置放置占位符。这样,若是须要回退,能够回退到某个占位符。

-- 建立保留点
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 。

  • MySQL用户帐号和信息存储在名为 mysql 的MySQL数据库中。
  • mysql 数据库有一个名为 user 的表,它包含全部用户帐号。
-- 查看当前全部用户
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 可在几个层次上控制访问权限:

  • 整个服务器,使用 GRANT ALL 和 REVOKE ALL;
  • 整个数据库,使用 ON database.*;
  • 特定的表,使用 ON database.table;
  • 特定的列;
  • 特定的存储过程。

1559027891075

数据库维护

数据库备份

  • msyqldump 程序
  • BACKUP TABLESELECT 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 命令终结某个特定的进程(使用这个命令须要做为管理员登陆)。
  • 使用 EXPLAIN 语句让MySQL解释它将如何执行一条 SELECT 语句。
  • 在导入数据时,应该关闭自动提交。你可能还想删除索引(包括FULLTEXT 索引),而后在导入完成后再重建它们。
  • 你的 SELECT 语句中有一系列复杂的 OR 条件吗?经过使用多条SELECT 语句和链接它们的 UNION 语句,你能看到极大的性能改进。
  • 索引改善数据检索的性能,但损害数据插入、删除和更新的性能。若是你有一些表,它们收集数据且不常常被搜索,则在有必要以前不要索引它们。(索引可根据须要添加和删除。)
  • LIKE 很慢。通常来讲,最好是使用 FULLTEXT 而不是 LIKE 。

加快数据库恢复速度

在恢复数据时,可能会导入大量的数据。

此时有一些技巧能够提升导入速度:

  • 导入时禁用索引, 导入结束后再开启索引

    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

# 分析慢日志
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 是候选键

主键 从候选键中选择一个, 也称为 主码.

外键 数据表中的字段是别的数据表的主键, 则称它为外键.

主属性 包含在任意候选键中的属性称为主属性.

范式

全部范式(按照严格顺序排列):

  • 1NF(第一范式)

    关键: 表中任何属性都是原子性的, 不可再分.

    解释: 字段不要是能够由其余字段组合/计算的.

  • 2NF(第二范式)

    须要保证表中的非主属性与候选键(码)彻底依赖 (即消除了部分依赖)

  • 3NF(第三范式)

    须要保证表中的非主属性与候选键(码)不存在传递依赖

    一般只要求到这个级别.

  • BCNF(巴斯-科德范式)

    消除主属性之间的部分依赖和传递依赖

  • 4NF(第四范式)
  • 5NF(完美范式)

这里的

  • 部分依赖 也称为 部分函数依赖
  • 传递依赖 也称为 传递函数依赖

一般要求: <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
文件可否移动 能,一张表就对应.frmMYDMYI3个文件 否,由于关联的还有data下的其它文件
记录存储顺序 按记录插入顺序保存 按主键大小有序插入
空间碎片(删除记录并flush table 表名以后,表文件大小不变) 产生。定时整理:使用命令optimize table 表名实现 不产生
事务 不支持 支持
外键 不支持 支持
锁支持(锁是避免资源争用的一个机制,MySQL锁对用户几乎是透明的) 表级锁定 行级锁定、表级锁定,锁定力度小并发能力高
锁扩展

表级锁(table-level lock):lock tables <table_name1>,<table_name2>... read/writeunlock 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会锁定id20左右如下的范围,你可能没法插入id1822的一条新纪录。

课程数据

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.'
);
相关文章
相关标签/搜索