这里主要列举一些以前没有注意过的地方:
不可见列通常对查询是隐藏的,但如果显式引用,可以被访问。
create table t_visible(
a int,
b int invisible
);
select * from t_visible ;
a|
-+
可以通过show columns from t_visible;查看Extra列来判断是否是隐藏列。通过select * from information_schema.COLUMNS c where EXTRA = ‘INVISIBLE’;可以查看所以得隐藏列。
CREATE TABLE会导致隐式提交,但是创建临时表并不会。
CREATE TABLE LIKE创建一个基于原始表的定义的空表,包括在原始表中定义的任何列属性和索引。
我们常见的SELECT语句创建表是这样的:
CREATE TABLE new_tbl [AS] SELECT * FROM orig_tbl;
其实还可以先指定一部分列:
create table t_create_t0(
a int
);
insert into t_create_t0 values(1);
create table t_create_t1(b int default 5) as select a from t_create_t0;
select * from t_create_t1;
b|a|
-+-+
5|1|
从MySQL 8.0.19开始,在CREATE TABLE … SELECT语句中可以使用VALUES语句作为一张表,这是一个很有用的功能。比如,有下面一张表t_book表:
select * from t_book tb ;
ID|NAME |
--+------+
0|Python|
2|Java |
给定一组用户名,希望将每个用户与全部的book数据关联并输出:
select t.*,tb.name from (values ROW('u1','上海'), row('u2','北京')) as t(name,address)
join t_book tb
name|address|name |
----+-------+------+
u2 |北京 |Python|
u1 |上海 |Python|
u2 |北京 |Java |
u1 |上海 |Java |
CREATE TABLE … SELECT并不会自动创建索引,但可以在CREATE TABLE 部分指定列属性,也可以创建索引。
CREATE TABLE … SELECT也不会保留生成列信息,但可以保留原表的表达式默认值。
CREATE TABLE … SELECT不会保留AUTO_INCREMENT ,但是会保留 NULL (NOT NULL) 、 CHARACTER SET, COLLATION, COMMENT以及DEFAULT属性。
在MySQL8.0.21前,表的创建与数据的插入是两部分。所以数据插入失败新表依然会创建,但是新的版本是一个事务,数据插入失败,创建的表也会被回滚。
外键约束名称必须是数据库内唯一的,如果不指定的话,那么会自动生成。
外键的使用有很多约束:
create table t_parent(
id int primary key,
name varchar(100) ,
index idx_p_name (name)
);
create table t_child(
id int primary key,
name varchar(100),
p_name varchar(100),
FOREIGN KEY(p_name) REFERENCES t_parent(name) on DELETE CASCADE
);
Table |Non_unique|Key_name|Seq_in_index|Column_name|Collation
-------+----------+--------+------------+-----------+---------
t_child| 0|PRIMARY | 1|id |A
t_child| 1|p_name | 1|p_name |A
alter table t_child add index nfk_p_name(p_name);
Table |Non_unique|Key_name |Seq_in_index|Column_name
-------+----------+----------+------------+-----------
t_child| 0|PRIMARY | 1|id
t_child| 1|nfk_p_name| 1|p_name
外键的参照动作:
外键检查是由foreign_key_checks变量控制的,这个默认是启用的。但是有时候是需要禁用的,禁用的好处有:
外键禁用后,仍然存在禁止的操作:
需要注意的是,禁用外键检查后,删除父表是被允许的,而且子表的外键约束自动删除。
查询已有外键的方式:
// 查询列是外键列:
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE REFERENCED_TABLE_SCHEMA IS NOT NULL;
// 查询外键,会给出引用表与列
SELECT * FROM INFORMATION_SCHEMA.INNODB_FOREIGN;
//查询表的外键列
SELECT * FROM INFORMATION_SCHEMA.INNODB_FOREIGN_COLS
在MySQL 8.0.16以前,检查约束的语法比较简单:
CHECK (expr)
MySQL8.0.16之后, 语法更加丰富:
[CONSTRAINT [symbol]] CHECK (expr) [[NOT] ENFORCED]
约束的名称(symbol)不指定的话,数据库会自动生成一个,名称的最大长度为64个字符,区分大小写但不区分重音。
约束条件(expr)需要指定为一个布尔表达式,表达式的值必须是TRUE或UNKNOWN(对NULL来说)。
默认约束具有ENFORCED属性,既强制执行。可以改为NOT ENFORCED,不强制执行约束。
约束可以是针对列的,也可以是针对表的。列的约束出现在一个列的定义中,并且只能参考该列。表约束不出现在列的定义中,它可以引用任何表的一个或多个列,允许向前引用出现在表定义后面的列。
之前说约束的名称是数据库内唯一的,但是有个例外,一个TEMPORARY表会隐藏一个同名的非TEMPORARY表,所以它也可以有相同的CHECK约束名称。这里提到TEMPORARY表可以重名以前不知道,验证一下:
create table t_not_tmp(
id int primary key,
c1 int CONSTRAINT `c1_positive` CHECK ((`c1` > 0))
);
create temporary table t_not_tmp(
i1 int ,
i2 int CONSTRAINT `c1_positive` CHECK ((`i2` > 0))
);
select * from t_not_tmp;
i1|i2|
--+--+
CHECK条件表达式必须遵守的规则:
对INSERT IGNORE, UPDATE IGNORE, LOAD DATA … IGNORE, 和 LOAD XML … IGNORE 语句,如果约束检查失败,则会出现警告,并跳过违规行的插入或更新。没有IGNORE的语句,约束检查失败的时候会报错。
需要注意:如果约束表达式中,出现与声明的列类型不同的数据类型,那么根据通常的MySQL类型转换规则,会发生对声明类型的隐式转化。
可以依据一个现有列的表达式,添加一个新的列,比如:
create table t_v_c(
first_name varchar(20),
last_name varchar(20),
full_name varchar(40) as (concat(first_name,' ', last_name)),
dateOfBirth datetime,
birthday varchar(5) as (date_format(dateOfBirth, '%m-%d')) STORED
);
insert into t_v_c(first_name,last_name,dateOfBirth) values('A','B','2020-01-02');
select * from t_v_c;
first_name|last_name|full_name|dateOfBirth |birthday|
----------+---------+---------+-------------------+--------+
A |B |A B |2020-01-02 00:00:00|01-02 |
生成列完整的定义语法:
col_name data_type [GENERATED ALWAYS] AS (expr)
[VIRTUAL | STORED] [NOT NULL | NULL]
[UNIQUE [KEY]] [[PRIMARY] KEY]
[COMMENT 'string']
生成列的表达式要求:
外键约束不能引用一个虚拟生成的列,虚拟列也不能添加外键约束,但是STORED生成列可以加上外键约束引用其他列,而且其上的外键约束不能使用CASCADE、SET NULL或SET DEFAULT作为ON UPDATE参考动作,也不能使用SET NULL或SET DEFAULT作为ON DELETE参考动作。
生成列有哪些作用呢:
InnoDB支持虚拟生成列的二级索引,也叫虚拟索引。虽然虚拟列的值是在查询的时候被计算的,但是当一个二级索引在虚拟生成列上被创建时,生成的列值在索引的记录中被具体化。
在虚拟列上使用二级索引时,由于在INSERT和UPDATE操作过程中在二级索引记录中物化虚拟列值时进行的计算,需要考虑额外的写入成本。但是,文档说在虚拟列上添加或删除一个二级索引是一个 in-place 操作,这意味着应该会比较快。
MySQL 8.0.30开始,如果创建表的时候没有指定主键,且开启了GIPK (generated invisible primary key)模式的话,mySQL会自动添加一个不可见的主键。
GIPK的开关由sql_generate_invisible_primary_key 变量控制,生成的不可见列将是这样的:
`my_row_id` bigint unsigned NOT NULL AUTO_INCREMENT /*!80023 INVISIBLE */,
PRIMARY KEY (`my_row_id`)
my_row_id是固定的,所以该模式下,尽量不要给一个没有主键的表使用这个名称作为列名。
当GIPK模式生效的时候,除了在可见和不可见之间切换之外,不能改变生成的主键。
由于是不可见列,所以select * 查询不到,需要明确指明这一列才可以查出来。