在此主要介绍下与数据库相关的一些操作,其中有一部分是与 MySQL 相关的功能。
简介
SQL-92 是最常用的 Structured Query Language, SQL 标准,其定义了一些常见的语法等,详细的可参考 SQL-92,常见的 SQL 操作包括如下:
DDL
数据库模式定义语言,create 。DML
数据操纵语言,insert、delete、update 。DCL
数据库控制语言 ,grant、remove 。DQL
数据库查询语言,select 。
不过不同的数据库对 SQL-92 标准会有所扩展,这也就造成了一些数据库操作的不兼容,或者是一些细节上的差异。
外键
创建外键的语法规则如下:
[CONSTRAINT symbol] FOREIGN KEY [id] (index_col_name, ...)
REFERENCES tbl_name (index_col_name, ...)
[ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION}]
[ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION}]
外键维护数据完整性的 5 种方式:
CASCADE
从父表删除或更新且自动删除或更新子表中匹配的行,可以为ON DELETE CASCADE
和ON UPDATE CASCADE
;SET NULL
从父表删除或更新行,设置子表中的外键列为NULL
,需要对应列也可为NULL
。NO ACTION
SQL-92 标准中,如果有外键的,那么删除或更新主键时会报错;RESTRICT
拒绝对父表的删除或更新操作。一些数据库有延期检查,一般NO ACTION
是一个延期检查;而在 MySQL 中,外键约束是被立即检查的,所以RESTRICT
与上述同样。SET DEFAULT
这个动作被解析程序识别,但 InnoDB 不支持包含ON DELETE SET DEFAULT
或ON UPDATE SET DEFAULT
子句的表定义。
如下是测试用例:
----- 创建用户表
mysql> CREATE TABLE `user` (
`id` int(11) NOT NULL auto_increment COMMENT '用户ID',
`name` varchar(50) NOT NULL default '' COMMENT '名字',
`sex` int(1) NOT NULL default '0' COMMENT '0为男,1为女',
PRIMARY KEY (`id`)
) ENGINE=innodb DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
----- 写入测试数据
mysql> INSERT INTO user(name, sex) VALUES("andy", 0),("lily", 1);
----- 创建订单表
mysql> CREATE TABLE `order` (
`order_id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '订单ID',
`u_id` INT(11) NOT NULL DEFAULT '0' COMMENT '用户ID',
`username` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '用户名',
`money` INT(11) NOT NULL DEFAULT '0' COMMENT '钱数',
`datetime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '生成时间',
PRIMARY KEY(`order_id`),
INDEX (`u_id`),
FOREIGN KEY order_f_key (u_id) REFERENCES user(id)
) ENGINE=innodb DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
mysql> INSERT INTO `order`(`u_id`, `username`, `money`, `datetime`)
VALUES('1', 'andy','10', CURRENT_TIMESTAMP);
----- 由于order表中有依赖,导致删除user表时失败
mysql> DELETE FROM user WHERE id =1;
----- 写入数据时由于user表中不存在,导致写入失败
mysql> INSERT INTO `order`(`u_id`, `username`, `money`, `datetime`)
VALUES('5', 'foobar', '123', CURRENT_TIMESTAMP);
----- MySQL中没有修改外键操作,需要先删除再重新添加
mysql> ALTER TABLE `order` DROP FOREIGN KEY order_ibfk_1;
mysql> ALTER TABLE `order` ADD FOREIGN KEY(u_id) REFERENCES user(id)
ON DELETE CASCADE ON UPDATE CASCADE;
mysql> REPLACE INTO `order`(`u_id`, `username`, `money`, `datetime`)
VALUES('2', 'andy','10', CURRENT_TIMESTAMP);
sql_mode
sql_mode
是个很容易被忽视的变量,在生产环境必须将这个值设置为严格模式,所以开发、测试环境的数据库也必须要设置,这样在开发测试阶段就可以发现问题。
----- 查看变量
mysql> SHOW VARIABLES LIKE 'sql_mode%'\G
*************************** 1. row ***************************
Variable_name: sql_mode
Value: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,
NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
1 row in set (0.01 sec)
----- 设置变量
mysql> SET GLOBAL sql_mode='';
简单列举如下常用的,详细可以参考官方手册 MySQL Reference Manual - Server SQL Modes 。
NO_ZERO_IN_DATE
在严格模式下,不允许日期和月份为零。NO_ZERO_DATE
不允许插入零日期,插入零日期会抛出错误而不是警告。NO_AUTO_CREATE_USER
禁止 GRANT 创建密码为空的用户。ONLY_FULL_GROUP_BY
参考如下。
ONLY_FULL_GROUP_BY
对于 GROUP BY
聚合操作,如果在 SELECT 中的列没有在 GROUP BY
中出现 (可以是聚合函数),那么认为这个 SQL 是不合法的。
例如 SELECT language_id, COUNT(1), length FROM film GROUP BY language_id;
是不合法的,原因是 MySQL 允许在 SELECT 列表中存在除聚集函数或 GROUP BY
以外的表达式,但是这些表达式的结果并不确定,通常也就没有太多意义,反而可能会导致错误。
实际上 SQLServer、Oracle、PostgreSQL 基本都不支持这种语法,为此,MySQL 5.7 修订了这一语义。另外,需要注意 SELECT id+1 FROM tt GROUP BY 1+id;
这种是不允许的,而 SELECT id+1 FROM tt GROUP BY id+1;
则可以。
其它
注释
在 MySQL 中可以通过 #
(从开始到结束)、/* ... */
(可以多行) 表示注释内容;从 MySQL-3.23 版本开始,可以通过 /*! ... */
这种特有的注释方式标示 MySQL 特有特性。
MySQL 会解析其中的关键字,而其它数据库则会视为注释,从而保证兼容性。另外,从 3.23 开始支持 "-- "
格式的注释,功能与 #
类似;需要注意的是,双短划线之后有一个空格。
接下来,重点说下 /*! ... */
这个注释,简言之,MySQL 会执行其中的内容,例如,MySQL 支持 STRAIGHT_JOIN
写法,而其它数据库可能不支持,那么就可以写成 SELECT /*! STRAIGHT_JOIN */ col1 FROM table1,table2 WHERE...
这种方式。
另外,可以在叹号后面添加版本号,表示只有当 MySQL 的版本大于等于该版本时才会执行,例如只有 MySQL-3.23.02 之后才支持 TEMPORARY,那么可以写为 CREATE /*!32302 TEMPORARY */ TABLE t (a INT);
。
其它的示例 CREATE DATABASE
blog /*!40100 DEFAULT CHARACTER SET latin1 */;
。
show tables
默认在使用 LIKE 过滤时,不能使用 AND/OR 等条件,可以通过如下方式添加过滤条件。
----- 只能使用单个like语句
mysql> SHOW TABLES LIKE 'host%';
----- 如果要使用多个,需要使用WHERE子句,其中FROM子句可以省略
mysql> SHOW TABLES FROM `sys` WHERE `Tables_in_sys` LIKE 'user%' OR `Tables_in_sys` LIKE 'host%';