使用MySQL Slow Log来解决MySQL CPU占用高的问题

但是怎么找到是哪个SQL语句的执行时间过长呢?可以通过MySQL Slow Log来找,详解如下。

首先找到MySQL的配置文件my.cnf,根据不同版本的mysql开启慢查询的配置也不一样

mysql 5.0 

[mysqld] 

long_query_time = 1 

log-slow-queries = /var/log/mysql/slow.log 

mysql 5.1 

[mysqld] 

long_query_time = 1 

slow_query_log=1 

slow_query_log_file = /var/log/mysql/slow.log 

long_query_time 是指执行超过多久的sql会被log下来,这里是1秒。 

log-slow-queries和slow_query_log_file 设置把日志写在哪里 

把上述参数打开,运行一段时间,就可以关掉了,省得影响生产环境

接下来就是分析了,我这里的文件名字叫 /var/log/mysql/slow.log。

先mysqldumpslow –help下,主要用的是

-s ORDER what to sort by (t, at, l, al, r, ar etc), ‘at' is default 

-t NUM just show the top n queries 

-g PATTERN grep: only consider stmts that include this string 

-s,是order的顺序,说明写的不够详细,主要有

c,t,l,r和ac,at,al,ar,分别是按照query次数,时间,lock的时间和返回的记录数来排序,前面加了a的时倒序

-t,是top n的意思,即为返回前面多少条的数据

-g,后边可以写一个正则匹配模式,大小写不敏感的

mysqldumpslow -s c -t 20 /var/log/mysql/slow.log 

mysqldumpslow -s r -t 20 /var/log/mysql/slow.log 

上述命令可以看出访问次数最多的20个sql语句和返回记录集最多的20个sql。

mysqldumpslow -t 10 -s t -g “left join” /var/log/mysql/slow.log 

这个是按照时间返回前10条里面含有左连接的sql语句。

用了这个工具就可以查询出来那些sql语句是性能的瓶颈,进行优化,比如加索引,该应用的实现方式等。

Mysql DNS反向解析导致连接超时过程分析(skip-name-resolve)

MySQL数据库收到一个网络连接后,首先拿到对方的IP地址,然后对这个IP地址进行反向DNS解析从而得到这个IP地址对应的主机名。用主机名在权限系统里面进行权限判断。反向DNS解析是耗费时间的,有可能让用户感觉起来很慢。甚至有的时候,反向解析出来的主机名并没有指向这个IP地址,这时候就无法连接成功了。

可以在配置文件里面禁止MySQL进行反向DNS解析,只需在my.cnf的[mysqld]段落中加入如下行即可:

skip-name-resolve (windows与linux下一样的)

设备在连接mysql时候,等待服务器的banner信息需要4s左右,影响了Mysql服务的连接速度。

通过如下方式进行验证:

1、Telnet端口验证

通过设备和虚拟机(Linux系统)分别Telnet Mysql服务的端口,会出现一下现象:

设备(UAG/SCANNER): telnet后,等待Mysql的服务器端回应大概需要等10s左右。

[DPtech-Developer-Shell]telnet 10.101.0.206 3308

Trying 10.101.0.206...

Connected to 10.101.0.206.

Escape character is '^]'.

E

5.0.67-community-nt-log?Hc95

虚拟机(Ubuntu):telnet后,立即得到了Mysql服务器的返回

[root]~# telnet 10.101.0.206 3308

Trying 10.101.0.206...

Connected to 10.101.0.206.

Escape character is '^]'.

E

5.0.67-community-nt-log?D%(;1$]+,¢!Zdh`'?G)6r]YConnection closed by foreign host.   //这里耗时很短

2、通过程序进行验证

具体源代码见附件:验证程序源代码

源代码基本上是设置了Recv超时后,建立socket连接之后接受数据,收到后计时并输出。

在设备上和虚拟机中的结果分别如下:

设备:

[DPtech-Developer-Shell]/tcpclient_mips 10.101.0.1 3306

花费时间:19553

Recved 68 bytes

@

5.5.2-m2-community%uD3q`n)

虚拟机:

[root]tcp_demo# ./tcpclient 10.101.0.1 3306

花费时间:10525

Recved 68 bytes

@

5.5.2-m2-communitd~k~Y";B

可以发现,设备上大约比Linux服务器多耗时9s,其中10秒钟可能是recv本身超时的时间。

3、通过不同操作系统进行Telnet验证

通过Windows系统和Linux虚拟机、设备,分别通过Telnet进行连接尝试,通过抓包分析得知,只有设备的耗时比较长,其他的耗时都比较短。

抓包时发现设备中的socket建立之后,MYSQL服务器需要发送很多次的NBNS报文后,才会传输banner信息,而Linux虚拟机和Windows系统的主机在这个过程中都没有出现这个问题。

查找了一些资料,关于MYSQL NBNS报文的问题:

Mysql论坛的提问:

http://forums.mysql.com/read.php?11,250982,250982#msg-250982

该问题的答复

http://forums.mysql.com/read.php?11,250982,254683#msg-254683

从答复中来看,貌似是某些版本的问题,临时的解决方案是对Mysql服务器进行配置,不启用Named Pipes,即 命名管道 功能即可解决这个问题。

后经查找相关资料得知,远程连接超时可能由于Mysql默认开启了DNS反向解析的缘故,每次连接时服务器都尝试解析连接客户端的主机名,导致时间比较长。

解决方法是在服务器端的my.ini文件中,[mysqld]这个节下配置一个skip-name-resolve以关闭Mysql默认开启的DNS反向解析就可以了。

再次通过设备和虚拟机或者Windows系统进行Telnet,可以发现连接超时的现象明显不存在了。

另外通过自己写的C代码进行连接的时候也存在同样的问题,修改skip-name-resolve以后,实际上就可以发现该问题已经不存在了:

设备:

[DPtech-Developer-Shell]/tcpclient_mips 10.101.0.1 3306 

花费时间:10520

Recved 68 bytes

@

5.5.2-m2-community[Z44E>G)

虚拟机:

[root]tcp_demo# ./tcpclient 10.101.0.1 3306

花费时间:10521

Recved 68 bytes

@

5.5.2-m2-community7evE5wyx

通过虚拟机Telnet连接另外一个ip 10.101.0.206时候发现速度也比较慢,消耗的时间基本上和设备中相当,可能是由于虚拟机和宿主主机之前不需要进行反向域名解析,或者说是应为系统本身就知道虚拟机IP地址(NAT模式)对应的主机名,所以不需要进行DNS反向解析,导致在虚拟机中出现了特殊情况。

最后得出结论,可能这个问题实际上和设备或者虚拟机,Linux系统、Windows系统没有多大关系,主要由于服务器的反向DNS解析导致该问题。无法从客户端途径去解决,也就是说我们设备无法处理这种情形。

mysql解决远程不能访问的二种方法

1、在/etc/mysql/my.cnf中的[mysqld]段注释掉bind-address = 127.0.0.1

2、用mysql -uroot -p 登陆mysql,然后采用以下方法开启远程访问权限:

方法1:mysql>use mysql;

mysql>update user set host = ‘%’ where user = ‘root’;

mysql>FLUSH RIVILEGES;

方法2:mysql>GRANT ALL PRIVILEGES ON . TO ‘myuser’@’%’ IDENTIFIED BY ‘mypassword’ WITH GRANT OPTION;

利用Xtrabackup工具备份及恢复(MySQL DBA的必备工具)

Xtrabackup——MySQL DBA的必备工具

注意:

  1. 文档参照http://www.percona.com/docs/wiki/percona-xtrabackup:start

  2. mysql要使用5.1.50版本或以上。

一、Xtrabackup简介及安装

1、Xtrabackup 是percona的一个开源项目,可以热备份innodb ,XtraDB,和MyISAM(会锁表),可以看做是InnoDB Hotbackup的免费替代品。

Percona Support for MySQL

未分类

参考:http://www.percona.com/mysql-support/

先看看如何安装Xtrabackup,最简单的安装方式是使用RPM包,不过想使用源代码方式安装的话,其安装方式有点古怪,因为它采用的在MySQL源代码上打补丁构建的方式安装的。

2、安装:

wget http://www.percona.com/downloads/XtraBackup/XtraBackup-1.4/Linux/binary/i686/

tar zxf xtrabackup-1.4.tar.gz

cd xtrabackup-1.4

./configure

make

进行到这里时,千万别make install,那样就会接着安装MySQL了,正确方法是:

cd innobase/xtrabackup/

make

make install

安装参照:http://www.percona.com/docs/wiki/percona-xtrabackup:installation:from-source

3、如此一来,就会在/usr/bin目录里安装上两个有用的工具:xtrabackup、innobackupex

  1. xtrabackup 只能备份InnoDB和XtraDB两种数据表,支持在线热备份,可以在不加锁的情况下备份Innodb数据表,不过此工具不能操作Myisam引擎表

  2. innobackupex 是一个脚本封装,封装了xtrabackup,能同时处理Innodb和Myisam,但在处理Myisam时需要加一个读锁。

按如上的介绍,由于操作Myisam时需要加读锁,这会堵塞线上服务的写操作,而Innodb没有这样的限制,所以数据库中Innodb表类型所占的比例越大,则越有利。实际应用中一般是直接使用innobackupex方法,它主要有三种操作方式,按手册中的介绍:

Usage:

innobackup [--sleep=MS] [--compress[=LEVEL]] [--include=REGEXP] [--user=NAME]

[--password=WORD] [--port=PORT] [--socket=SOCKET] [--no-timestamp]

[--ibbackup=IBBACKUP-BINARY] [--slave-info] [--stream=tar]

[--defaults-file=MY.CNF]

[--databases=LIST] [--remote-host=HOSTNAME] BACKUP-ROOT-DIR

innobackup --apply-log [--use-memory=MB] [--uncompress] [--defaults-file=MY.CNF]

[--ibbackup=IBBACKUP-BINARY] BACKUP-DIR

innobackup --copy-back [--defaults-file=MY.CNF] BACKUP-DIR

——————————————————————————————————

第一个命令行是热备份mysql数据库。

带有–apply-log选项的命令是准备在一个备份上启动mysql服务。

带有–copy-back选项的命令从备份目录拷贝数据,索引,日志到my.cnf文件里规定的初始位置。

Xtrabackup还可以用来moving InnoDB tables between servers,更多的内容可以参考官方文档及例子。

参考链接:

  1. 官方文档:http://www.percona.com/docs/wiki/percona-xtrabackup:xtrabackup_manual

  2. Xtrabackup online backup for InnoDB/XTraDB(pdf):

http://www.percona.com/ppc2009/PPC2009_xtrabackup.pdf

二、innobackupex 和 xtrabackup备份详解

注:innobackupex会根据/et/my.cnf来确定MySQL的数据位置。

1. 普通备份:

innobackupex [--defaults-file=/etc/my.cnf] --user=root [--host=192.168.1.52] [--password=xxx] [--port=3306]   /data/back_data/    2>/data/back_data/1.log

备份的目录是/data/back_data/,这里的2>/data/back_data/1.log,是将备份过程中的输出信息重定向到1.log

innobackupex-1.5.1 –slave-info …..

–slave-info会记录复制主日志的 复制点,便于重新做复制用。(用在备份从机器用)

备份后的文件:

xtrabackup_binlog_info — 存放binlog的信息。(binlog需要另外拷贝备份,如果需要binlog的话)

xtrabackup_checkpoints — 存放备份的起始位置和结束位置。

恢复:

首先停掉数据库,然后删除数据库目录下的所有数据库文件.

cd /data/mysql_data

rm -rf * # 删除数据目录里的所有文件

innobackupex-1.5.1 --user=root --apply-log /data/back_data/2010-10-26_16-09-37 # 应用日志

innobackupex-1.5.1 --user=root --copy-back /data/back_data/2010-10-26_16-09-37

默认innobackupex-1.5.1会将二进制日志信息存放在文件xtrabackup_binlog_info中发(方便做Slave)。

cd /data

chown -R mysql:mysql mysql_data/

重启mysql服务

2. 打包(Tar)备份:

innobackupex-1.5.1 --user=root [--password=xxx] --stream=tar /data/back_data/2/ 2>/data/back_data/2.log   1>/data/back_data/2.tar

还原:

#cd /data/back_data/2/

#tar ixvf 2.tar

# ls

2.tar backup-my.cnf ibdata1 ibdata2 mablevi mysql xtrabackup_binlog_info xtrabackup_checkpoints  xtrabackup_logfile

准备还原

# innobackupex-1.5.1 --user=xxx [--password=xxx]--apply-log /data/back_data/

……

innobackupex: completed OK!

删除数据目录里的所有文件

rm -rf /data/mysql_data/*

拷贝:

# innobackupex-1.5.1 --user=xxx [--password=xxx] --copy-back  /data/back_data/

……

innobackupex: completed OK!

cd /data

chown -R mysql:mysql mysql_data/

重启mysql服务

3. 压缩(tar gzip)备份

innobackupex-1.5.1 --user=root [--password=xxx] --stream=tar

/data/back_data/2/  2>/data/back_data/2.log | gzip > /data/back_data/2.tar.gz

这里使用了管道|将innobackupex-1.5.1作为gzip的标准输入。恢复,只需要使用tar -izxvf 解压对应的文件后,操作完全同普通备份。

还原:

使用tar –izxvf 解压对应的文件后,操作完全同普通备份。

#cd /data/back_data/2/

#tar ixvf 2.tar

# ls

backup-my.cnf ibdata1 ibdata2 mablevi mysql xtrabackup_binlog_info xtrabackup_checkpoints  xtrabackup_logfile

准备还原:

# innobackupex-1.5.1 --user=xxx [--password=xxx] --apply-log /data/back_data/2/

……

innobackupex: completed OK!

删除数据目录里的所有文件

rm -rf /data/mysql_data/*

# innobackupex-1.5.1 --user=xxx [--password=xxx] --copy-back   /data/back_data/2/

cd /data

chown -R mysql:mysql mysql_data/

重启mysql服务

———————————————————————————————————————

xtrabackup 备份和恢复

备份:

xtrabackup --defaults-file=/etc/my.cnf --backup --target-dir=/data/back_data/

恢复:

需要执行两次xtrabackup –prepare

xtrabackup --defaults-file=/etc/my.cnf --prepare --target-dir=/data/back_data/

xtrabackup --defaults-file=/etc/my.cnf --prepare --target-dir=/data/back_data/

注意,xtrabackup只备份数据文件,并不备份数据表结构(.frm),所以使用xtrabackup恢复的时候,你必须有对应表结构文件(.frm)。

增量备份:

1、全量备份

xtrabackup --defaults-file=/etc/my.cnf --backup --target-dir=/data/back_data/

2、增量备份

xtrabackup --defaults-file=/etc/my.cnf --backup --target-dir=/data/back_data_inc/ --incremental-

basedir=/data/back_data/

在增量备份的目录下,数据文件都是以.delta结尾的。增量备份只备份上一次全量备份后被修改过的page,所以增量备份只暂用较少的空间。增量备份可以在增量备份的基础上增量。

增量备份恢复:

我们需要分别对全量、增量备份各做一次prepare操作。

xtrabackup --defaults-file=/etc/my.cnf --prepare --target-dir=/data/back_data/2010-10-26_16-09-37

xtrabackup --prepare --target-dir=/data/back_data/2010-10-26_16-09-37--incremental-

dir=/data/back_data_inc

xtrabackup --prepare --target-dir=/data/back_data/ #这一步不是必须的

这样,/data/back_data/下的数据文件就可以直接放到你的MySQL数据目录下,恢复数据了。

再次提醒,xtrabackup只备份InnoDB数据文件,表结构是不备份的,所以恢复的时候,你必须有对应表结构文件(.frm)。

rm -rf /data/mysql_data/ib*

cp -i /data/back_data/2010-10-26_16-09-37/ib* /data/mysql_data/

cd /data

chown -R mysql:mysql mysql_data/

3、innobackupex 与 xtrabackup 相结合

首先,innobackupex全备份:

innobackupex  --user=root /data/back_data/ 2>/data/back_data/1.log #会生成一个时间文件夹,这里假如是2010-10-29_15-57-44

然后,xtrabackup 做增量备份:

xtrabackup --defaults-file=/etc/my.cnf --backup --target-dir=/data/back_data_inc/4 --incremental-basedir=/data/back_data/2010-10-29_15-57-44

恢复:

首先停掉数据库,备份二进制日志(如果有的话),然后删除数据库目录下的所有数据库文件.

cd /data/mysql_data

rm -rf * # 删除数据目录里的所有文件

恢复全量备份:

innobackupex  --user=root --apply-log /data/back_data/2010-10-29_15-57-44 # 应用日志

innobackupex  --user=root --copy-back /data/back_data/2010-10-29_15-57-44 # 拷贝文件

恢复增量备份:

xtrabackup --prepare --target-dir=/data/back_data/2010-10-29_15-57-44 --incremental-dir=/data/back_data_inc/5

cd /data

chown -R mysql:mysql mysql_data/

重启mysql服务。

MySQL笔记之数据备份与还原的使用详解

数据很重要,这点用脚趾头想都知道,为了保证数据的安全,因此需要定期对数据备份

下面来写一点关于数据备份与还原相关的笔记

数据备份

使用mysqldump命令备份一个数据库

mysqldump -u username -p dbname table1 table2...>BackupName.sql

其中,dbname表示数据库名称,table1和table2表示表的名称,没有该参数则备份整个数据库

BackupName.sql参数表示备份文件的名称,文件名前面可以加上一个绝对路径

需要注意的是,备份的时候只备份数据库中的表,如果要还原则必须还原在已有数据库中

tips:通常情况下备份文件的后缀名为.sql,当然也可以是.txt等,只不过不专业

使用mysqldump命令备份多个数据库

mysqldump -u username -p--databases dbname1 dbname2...>BackupName.sql

此处–databases代表备份多个数据库的意思,后面接数据库名

此外还能备份所有数据库

使用mysqldump命令备份所有数据库

mysqldump -u username -p--all-databases>BackupName.sql

直接复制整个数据库目录

直接复制数据库目录是最简单的备份方法,但并非最好

使用这种方法时为了避免数据变化需要暂时停止数据库服务,但实际中不允许

而且,这种方法对InnoDB存储引擎的表不适用,对于MyISAM存储引擎的表比较方便

数据还原

使用mysql命令还原

 mysql -u root -p[dbname]<backup.sql

[dbname]是可选的,如果使用–all-databases参数备份整个数据库而不是单独备份表

那么便可以不使用dbname参数

直接复制到数据库目录

使用这种方式必须保证数据库的主版本号一致

通常对MyISAM比较有效,对InnoDB不适用。

MySQL笔记之别名的使用

在查询时,可以为表和字段取一个别名。这个别名可以代替其指定的表和字段

为表取别名

mysql> SELECT * FROM department d
    -> WHERE d.d_id=1001;
+------+-----------+--------------+-------------+
| d_id | d_name    | function     | address     |
+------+-----------+--------------+-------------+
| 1001 | 科技部    | 研发产品       | 3号楼5层     |
+------+-----------+--------------+-------------+
 row in set (0.00 sec)

这里为department表取了一个别名为d

为字段取别名

mysql> SELECT d_id AS department_id, d_name AS department_name
    -> FROM department;
+---------------+-----------------+
| department_id | department_name |
+---------------+-----------------+
|          1001 | 科技部          |
|          1002 | 生产部          |
|          1003 | 销售部          |
+---------------+-----------------+
 rows in set (0.00 sec)

此处语法与上面类似,只不过加了AS。

MySQL笔记之子查询使用介绍

子查询是将一个查询语句嵌套在另一个查询语句中

内层查询语句的查询结果,可以为外层查询语句提供查询条件

因为在特定情况下,一个查询语句的条件需要另一个查询语句来获取

参考表:employee

未分类

参考表:department

未分类

带IN关键字的子查询

mysql> SELECT * FROM employee
    -> WHERE d_id IN
    -> (SELECT d_id FROM department);
+------+------+--------+------+------+--------------------+
| num  | d_id | name   | age  | sex  | homeaddr           |
+------+------+--------+------+------+--------------------+
|    1 | 1001 | 张三   |   26 | 男   | 北京市海淀区         |
|    2 | 1001 | 李四   |   24 | 女   | 北京市昌平区         |
|    3 | 1002 | 王五   |   25 | 男   | 湖南长沙市           |
+------+------+--------+------+------+--------------------+
 rows in set (0.00 sec)

此处首先查询出department表中所有d_id字段的信息,并将结果作为条件

接着查询employee表中以d_id为条件的所有字段信息

NOT IN的效果与上面刚好相反

带比较运算符的子查询

mysql> SELECT d_id, d_name FROM department
    -> WHERE d_id!=
    -> (SELECT d_id FROM employee WHERE age=24);
+------+-----------+
| d_id | d_name    |
+------+-----------+
| 1002 | 生产部    |
| 1003 | 销售部    |
+------+-----------+
 rows in set (0.00 sec)

这里查询出了哪些部门没有年龄为24岁的员工,看起来有点复杂

此外,运算符还有很多,这里不再赘述

带EXISTS关键字的子查询

EXISTS关键字表示存在。使用EXISTS关键字时,内层查询语句不返回查询的记录,而是返回一个真假值,如果内层查询语句查询到满足条件的记录,就返回true,否则返回false

当返回的值为true时,外层查询语句将进行查询,否则不进行查询

mysql> SELECT * FROM employee
    -> WHERE EXISTS
    -> (SELECT d_name FROM department WHERE d_id=1004);
Empty set (0.00 sec)

此处内层循环并没有查询到满足条件的结果,因此返回false,外层查询不执行

NOT EXISTS刚好与之相反

当然,EXISTS关键字可以与其他的查询条件一起使用

条件表达式与EXISTS关键字之间用AND或者OR来连接

mysql> SELECT * FROM employee
    -> WHERE age>24 AND EXISTS
    -> (SELECT d_name FROM department WHERE d_id=1003);
+------+------+--------+------+------+--------------------+
| num  | d_id | name   | age  | sex  | homeaddr           |
+------+------+--------+------+------+--------------------+
|    1 | 1001 | 张三   |   26 | 男   | 北京市海淀区         |
|    3 | 1002 | 王五   |   25 | 男   | 湖南长沙市           |
+------+------+--------+------+------+--------------------+
 rows in set (0.00 sec)

带ANY关键字的子查询

ANY关键字表示满足其中任一条件

mysql> SELECT * FROM employee
    -> WHERE d_id!=ANY
    -> (SELECT d_id FROM department);
+------+------+--------+------+------+--------------------+
| num  | d_id | name   | age  | sex  | homeaddr           |
+------+------+--------+------+------+--------------------+
|    1 | 1001 | 张三   |   26 | 男   | 北京市海淀区         |
|    2 | 1001 | 李四   |   24 | 女   | 北京市昌平区         |
|    3 | 1002 | 王五   |   25 | 男   | 湖南长沙市           |
|    4 | 1004 | Aric   |   15 | 男   | England            |
+------+------+--------+------+------+--------------------+
 rows in set (0.00 sec)

带ALL关键字的子查询

ALL关键字表示满足其中所有条件

mysql> SELECT * FROM employee
    -> WHERE d_id>=ALL
    -> (SELECT d_id FROM department);
+------+------+------+------+------+----------+
| num  | d_id | name | age  | sex  | homeaddr |
+------+------+------+------+------+----------+
|    4 | 1004 | Aric |   15 | 男   | England  |
+------+------+------+------+------+----------+
 row in set (0.00 sec)

暂时不明白这两条语句是什么意思,到时候再补上

UNION合并查询结果

mysql> SELECT d_id FROM employee
    -> UNION
    -> SELECT d_id FROM department;
+------+
| d_id |
+------+
| 1001 |
| 1002 |
| 1004 |
| 1003 |
+------+
 rows in set (0.00 sec)

合并比较好理解,也就是将多个查询的结果合并在一起,然后去除其中的重复记录。

如果想保存重复记录可以使用UNION ALL语句。

MySQL笔记之触发器的应用

创建触发器

创建只有一个执行语句的触发器

CREATE TRIGGER 触发器名 BEFORE|AFTER 触发事件
ON 表名 FOR EACH ROW 执行语句

其中,触发器名参数指要创建的触发器的名字

BEFORE和AFTER参数指定了触发执行的时间,在事件之前或是之后

FOR EACH ROW表示任何一条记录上的操作满足触发事件都会触发该触发器

mysql> CREATE TRIGGER trig1 AFTER INSERT
    -> ON work FOR EACH ROW
    -> INSERT INTO time VALUES(NOW());
Query OK, 0 rows affected (0.09 sec)

上面创建了一个名为trig1的触发器,一旦在work中有插入动作,就会自动往time表里插入当前时间

创建有多个执行语句的触发器

CREATE TRIGGER 触发器名 BEFORE|AFTER 触发事件
ON 表名 FOR EACH ROW
BEGIN
    执行语句列表
END

其中,BEGIN与END之间的执行语句列表参数表示需要执行的多个语句,不同语句用分号隔开

tips:一般情况下,mysql默认是以 ; 作为结束执行语句,与触发器中需要的分行起冲突

为解决此问题可用DELIMITER,如:DELIMITER ||,可以将结束符号变成||

当触发器创建完成后,可以用DELIMITER ;来将结束符号变成;

mysql> DELIMITER ||
mysql> CREATE TRIGGER trig2 BEFORE DELETE
    -> ON work FOR EACH ROW
    -> BEGIN
    -> INSERT INTO time VALUES(NOW());
    -> INSERT INTO time VALUES(NOW());
    -> END
    -> ||
Query OK, 0 rows affected (0.06 sec)

mysql> DELIMITER ;

上面的语句中,开头将结束符号定义为||,中间定义一个触发器,一旦有满足条件的删除操作

就会执行BEGIN和END中的语句,接着使用||结束

最后使用DELIMITER ; 将结束符号还原

查看触发器

SHOW TRIGGERS语句查看触发器信息

mysql> SHOW TRIGGERSG;
*************************** 1. row ***************************
             Trigger: trig1
               Event: INSERT
               Table: work
           Statement: INSERT INTO time VALUES(NOW())
              Timing: AFTER
             Created: NULL
            sql_mode: 
             Definer: root@localhost
character_set_client: utf8
collation_connection: utf8_general_ci
  Database Collation: latin1_swedish_ci

结果会显示所有触发器的基本信息

tips:SHOW TRIGGERS语句无法查询指定的触发器

在triggers表中查看触发器信息

mysql> SELECT * FROM information_schema.triggersG
*************************** 1. row ***************************
           TRIGGER_CATALOG: def
            TRIGGER_SCHEMA: person
              TRIGGER_NAME: trig1
        EVENT_MANIPULATION: INSERT
      EVENT_OBJECT_CATALOG: def
       EVENT_OBJECT_SCHEMA: person
        EVENT_OBJECT_TABLE: work
              ACTION_ORDER: 0
          ACTION_CONDITION: NULL
          ACTION_STATEMENT: INSERT INTO time VALUES(NOW())

结果显示了所有触发器的详细信息,同时,该方法可以查询制定触发器的详细信息

mysql> SELECT * FROM information_schema.triggers WHERE TRIGGER_NAME='trig1'G
*************************** 1. row ***************************
           TRIGGER_CATALOG: def
            TRIGGER_SCHEMA: person
              TRIGGER_NAME: trig1
        EVENT_MANIPULATION: INSERT
      EVENT_OBJECT_CATALOG: def
       EVENT_OBJECT_SCHEMA: person
        EVENT_OBJECT_TABLE: work

tips:所有触发器信息都存储在information_schema数据库下的triggers表中

可以使用SELECT语句查询,如果触发器信息过多,最好通过TRIGGER_NAME字段指定查询

删除触发器

mysql> DROP TRIGGER trig1;
Query OK, 0 rows affected (0.04 sec)

删除触发器之后最好使用上面的方法查看一遍

同时,也可以使用database.trig来指定某个数据库中的触发器

tips:如果不需要某个触发器时一定要将这个触发器删除,以免造成意外操作

MySQL笔记之字符串函数的应用

字符串操作在程序设计中是非常重要的组成部分,而MySQL数据库中的字符串操作却相当简单

需要注意的是:下面所有的函数只是将修改后的结果当查询返回,均不对原数据做出改变

选出指定数量字符

mysql> SELECT RIGHT('nihao',3);
+------------------+
| RIGHT('nihao',3) |
+------------------+
| hao              |
+------------------+
 row in set (0.00 sec)

这里的RIGHT()函数代表从字符串中选定从右往左数的三个字符

与此类似,还有LEFT()函数

SUBSTRING_INDEX截取字符串

mysql> SELECT SUBSTRING_INDEX('HH,MM,SS',',',2);
+-----------------------------------+
| SUBSTRING_INDEX('HH,MM,SS',',',2) |
+-----------------------------------+
| HH,MM                             |
+-----------------------------------+
 row in set (0.00 sec)

此函数内部的第一个参数代表需要截取的内容,第二个参数代表按什么截取

最后一个是截取到第几个,1是截取到第一个逗号,2是截取到第二个

SUBSTRING截取字符串

mysql> SELECT SUBSTRING('helloworld',1,5);
+-----------------------------+
| SUBSTRING('helloworld',1,5) |
+-----------------------------+
| hello                       |
+-----------------------------+
 row in set (0.00 sec)

此处截取字符串中1~5的内容

UPPER字符串改大写

mysql> SELECT UPPER('hello');
+----------------+
| UPPER('hello') |
+----------------+
| HELLO          |
+----------------+
 row in set (0.00 sec)

LOWER字符串改小写

mysql> SELECT LOWER('HELLO');
+----------------+
| LOWER('HELLO') |
+----------------+
| hello          |
+----------------+
 row in set (0.00 sec)

REVERSE反转字符串

mysql> SELECT REVERSE('hello');
+------------------+
| REVERSE('hello') |
+------------------+
| olleh            |
+------------------+
 row in set (0.00 sec)

LTRIM清除左边多余空格

mysql> SELECT LTRIM('  hello     ');
+-----------------------+
| LTRIM('  hello     ') |
+-----------------------+
| hello                 |
+-----------------------+
 row in set (0.00 sec)

此外还有RTRIM清除右边空格,TRIM清除两边全部空格

LENGTH返回字符串中的字符数量

mysql> SELECT LENGTH('helo');
+----------------+
| LENGTH('helo') |
+----------------+
|              4 |
+----------------+
 row in set (0.00 sec)

MySQL笔记之连接查询详解

连接查询是将两个或两个以上的表按某个条件连接起来,从中选取需要的数据

当不同的表中存在表示相同意义的字段时,可以通过该字段来连接这几张表

参考表:employee

未分类

参考表:department

未分类

可以看到,上面两张表都有同一个字段d_id

当两张表含有相同意义的字段(可以不同名)时就可以进行连接查询

内连接查询

mysql> SELECT num, name, employee.d_id, sex, d_name, function
    -> FROM employee, department
    -> WHERE employee.d_id=department.d_id;
+------+--------+------+------+-----------+--------------+
| num  | name   | d_id | sex  | d_name    | function     |
+------+--------+------+------+-----------+--------------+
|    1 | 张三   | 1001 | 男   | 科技部    | 研发产品     |
|    2 | 李四   | 1001 | 女   | 科技部    | 研发产品     |
|    3 | 王五   | 1002 | 男   | 生产部    | 生产产品     |
+------+--------+------+------+-----------+--------------+
 rows in set (0.00 sec)

内连接查询只会查询完全匹配的结果,此处使用d_id字段进行连接

下面的表中也同样使用这一字段

外连接查询

外查询也需要通过指定字段来进行连接,当该字段取值相等时,可以查询出该记录

而且,该字段取值不相等的记录也可以查询出来

外连接查询包括左连接查询和右连接查询

左连接查询

mysql> SELECT num, name, employee.d_id, d_name, function
    -> FROM employee LEFT JOIN department
    -> ON employee.d_id=department.d_id;
+------+--------+------+-----------+--------------+
| num  | name   | d_id | d_name    | function     |
+------+--------+------+-----------+--------------+
|    1 | 张三   | 1001 | 科技部    | 研发产品     |
|    2 | 李四   | 1001 | 科技部    | 研发产品     |
|    3 | 王五   | 1002 | 生产部    | 生产产品     |
|    4 | Aric   | 1004 | NULL      | NULL         |
+------+--------+------+-----------+--------------+
 rows in set (0.00 sec)

此处不仅查询出了两表中d_id字段相匹配的信息

并且通过LEFT JOIN查询出了employee表中所有指定字段的信息

由于Aric没有对应d_name和function信息,所以显示null

右连接查询

mysql> SELECT num, name, employee.d_id, d_name, function
    -> FROM employee RIGHT JOIN department
    -> ON employee.d_id=department.d_id;
+------+--------+------+-----------+--------------+
| num  | name   | d_id | d_name    | function     |
+------+--------+------+-----------+--------------+
|    1 | 张三   | 1001 | 科技部    | 研发产品     |
|    2 | 李四   | 1001 | 科技部    | 研发产品     |
|    3 | 王五   | 1002 | 生产部    | 生产产品     |
| NULL | NULL   | NULL | 销售部    | 策划销售     |
+------+--------+------+-----------+--------------+
 rows in set (0.00 sec)

与上面相反,这里查询出了匹配的信息和department表中的所有指定字段的信息

但是由于employee表中部分字段没有对应,因此最后一行记录有显示NULL

复合条件连接查询

mysql> SELECT num, name, employee.d_id, sex, age, address
    -> FROM employee, department
    -> WHERE employee.d_id=department.d_id
    -> AND age>=25;
+------+--------+------+------+------+-------------+
| num  | name   | d_id | sex  | age  | address     |
+------+--------+------+------+------+-------------+
|    1 | 张三   | 1001 | 男   |   26 | 3号楼5层    |
|    3 | 王五   | 1002 | 男   |   25 | 5号楼1层    |
+------+--------+------+------+------+-------------+
 rows in set (0.00 sec)

复合条件连接查询是在进行连接查询的时候加入限制条件,此处的age>=25便是

通常情况下,限制条件越多,查询越精确,限制条件可用AND累加

此外,还可以用复合条件进行ORDER BY 排序

tips:连接查询中使用最多的是内连接查询,而外连接查询使用频率较低