15.1.20.4 创建表...选择语句
可以在SELECT
语句的末尾添加一个CREATE TABLE
语句来创建一个表:
CREATE TABLE new_tbl [AS] SELECT * FROM orig_tbl;
MySQL 为所有SELECT
元素创建新的列。例如:
mysql> CREATE TABLE test (a INT NOT NULL AUTO_INCREMENT,
-> PRIMARY KEY (a), KEY(b))
-> ENGINE=InnoDB SELECT b,c FROM test2;
这创建了一个InnoDB
表,具有三个列:a
、b
和c
。ENGINE
选项是CREATE TABLE
语句的一部分,不应该在SELECT
语句后使用;这将导致语法错误。同样,对于其他CREATE TABLE
选项,如CHARSET
也是一样。
注意,来自SELECT
语句的列被追加到表的右侧,而不是覆盖它。以下是一个示例:
mysql> SELECT * FROM foo;
+---+
| n |
+---+
| 1 |
+---+
mysql> CREATE TABLE bar (m INT) SELECT n FROM foo;
Query OK, 1 row affected (0.02 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM bar;
+------+---+
| m | n |
+------+---+
| NULL | 1 |
+------+---+
1 row in set (0.00 sec)
对于表foo
中的每一行,一个行将在bar
中插入,使用foo
的值和新列的默认值。
在CREATE TABLE ... SELECT
生成的表中,仅在CREATE TABLE
部分命名的列排在前面。同时出现在CREATE TABLE
和SELECT
部分命名的列排在后面。同时出现在SELECT
部分命名的列的数据类型可以被CREATE TABLE
部分override。
对于支持原子DDL和外键约束的存储引擎,在使用行级别复制时,CREATE TABLE ... SELECT
语句中不允许创建外键约束。可以使用ALTER TABLE
后添加外键约束。
您可以在SELECT
前面添加IGNORE
或REPLACE
,以指示如何处理唯一键值重复的行。使用IGNORE
,重复唯一键值的行将被忽略;使用REPLACE
,新行将替换具有相同唯一键值的行。如果既不指定IGNORE
也不指定REPLACE
,重复唯一键值将导致错误。更多信息,请参见忽略语句执行的影响。
您还可以在VALUES
语句中使用SELECT
部分的CREATE TABLE ... SELECT
;VALUES
语句的一部分必须包含一个使用AS
子句的表别名。要为来自VALUES
的列命名,提供表别名中的列别名;否则,将使用默认列名column_0
、column_1
、column_2
等。
否则,创建的表中的列命名遵循本节前面描述的规则。示例:
mysql> CREATE TABLE tv1
> SELECT * FROM (VALUES ROW(1,3,5), ROW(2,4,6)) AS v;
mysql> TABLE tv1;
+----------+----------+----------+
| column_0 | column_1 | column_2 |
+----------+----------+----------+
| 1 | 3 | 5 |
| 2 | 4 | 6 |
+----------+----------+----------+
mysql> CREATE TABLE tv2
> SELECT * FROM (VALUES ROW(1,3,5), ROW(2,4,6)) AS v(x,y,z);
mysql> TABLE tv2;
+---+---+---+
| x | y | z |
+---+---+---+
| 1 | 3 | 5 |
| 2 | 4 | 6 |
+---+---+---+
mysql> CREATE TABLE tv3 (a INT, b INT, c INT)
> SELECT * FROM (VALUES ROW(1,3,5), ROW(2,4,6)) AS v(x,y,z);
mysql> TABLE tv3;
+------+------+------+----------+----------+----------+
| a | b | c | x | y | z |
+------+------+------+----------+----------+----------+
| NULL | NULL | NULL | 1 | 3 | 5 |
| NULL | NULL | NULL | 2 | 4 | 6 |
+------+------+------+----------+----------+----------+
mysql> CREATE TABLE tv4 (a INT, b INT, c INT)
> SELECT * FROM (VALUES ROW(1,3,5), ROW(2,4,6)) AS v(x,y,z);
mysql> TABLE tv4;
+------+------+------+---+---+---+
| a | b | c | x | y | z |
+------+------+------+---+---+---+
| NULL | NULL | NULL | 1 | 3 | 5 |
| NULL | NULL | NULL | 2 | 4 | 6 |
+------+------+------+---+---+---+
mysql> CREATE TABLE tv5 (a INT, b INT, c INT)
> SELECT * FROM (VALUES ROW(1,3,5), ROW(2,4,6)) AS v(a,b,c);
mysql> TABLE tv5;
+------+------+------+
| a | b | c |
+------+------+------+
| 1 | 3 | 5 |
| 2 | 4 | 6 |
+------+------+------+
当选择所有列并使用默认列名时,可以省略SELECT *
,因此用于创建表tv1
的语句也可以写成以下形式:
mysql> CREATE TABLE tv1 VALUES ROW(1,3,5), ROW(2,4,6);
mysql> TABLE tv1;
+----------+----------+----------+
| column_0 | column_1 | column_2 |
+----------+----------+----------+
| 1 | 3 | 5 |
| 2 | 4 | 6 |
+----------+----------+----------+
使用VALUES
作为SELECT
的来源时,所有列总是被选择到新表中,并且不能单独选择某些列;以下每个语句都将产生错误(ER_OPERAND_COLUMNS
):
CREATE TABLE tvx
SELECT (x,z) FROM (VALUES ROW(1,3,5), ROW(2,4,6)) AS v(x,y,z);
CREATE TABLE tvx (a INT, c INT)
SELECT (x,z) FROM (VALUES ROW(1,3,5), ROW(2,4,6)) AS v(x,y,z);
类似地,你也可以使用TABLE
语句代替SELECT
。这个跟使用VALUES
一样;所有源表的列和源表中的列名总是被插入到新表中。示例:
mysql> TABLE t1;
+----+----+
| a | b |
+----+----+
| 1 | 2 |
| 6 | 7 |
| 10 | -4 |
| 14 | 6 |
+----+----+
mysql> CREATE TABLE tt1 TABLE t1;
mysql> TABLE tt1;
+----+----+
| a | b |
+----+----+
| 1 | 2 |
| 6 | 7 |
| 10 | -4 |
| 14 | 6 |
+----+----+
mysql> CREATE TABLE tt2 (x INT) TABLE t1;
mysql> TABLE tt2;
+------+----+----+
| x | a | b |
+------+----+----+
| NULL | 1 | 2 |
| NULL | 6 | 7 |
| NULL | 10 | -4 |
| NULL | 14 | 6 |
+------+----+----+
因为底层SELECT
语句的行顺序不能总是确定,CREATE TABLE ... IGNORE SELECT
和 CREATE TABLE ... REPLACE SELECT
语句被标记为不安全的语句,对于语句基于的复制模式下在错误日志中产生警告,并且在使用混合模式时使用行基于的格式写入二进制日志。请参见第19.2.1.1节,“语句基于和行基于复制的优缺”。
CREATE TABLE ... SELECT
不会自动创建索引。这样做是为了使语句尽量灵活。如果你想在创建的表中有索引,应该在SELECT
语句之前指定这些索引:
mysql> CREATE TABLE bar (UNIQUE (n)) SELECT n FROM foo;
对于 CREATE TABLE ... SELECT
,目标表不保留选取的表中的生成列信息。SELECT
部分不能将值赋予目标表中的生成列。
对于 CREATE TABLE ... SELECT
,目标表保留原始表的表达式默认值。
可能会发生数据类型的转换。例如,AUTO_INCREMENT
属性不被保留,VARCHAR
列可能变为 CHAR
列。保留的属性是 NULL
(或 NOT NULL
)和,具有这些列的 CHARACTER SET
、COLLATION
、COMMENT
和 DEFAULT
子句。
在使用CREATE TABLE ... SELECT
创建表时,确保将函数调用或表达式别名化。如果不这样做,CREATE
语句可能失败或产生不良的列名称。
CREATE TABLE artists_and_works
SELECT artist.name, COUNT(work.artist_id) AS number_of_works
FROM artist LEFT JOIN work ON artist.id = work.artist_id
GROUP BY artist.id;
你也可以在创建的表中显式指定列的数据类型:
CREATE TABLE foo (a TINYINT NOT NULL) SELECT b+1 AS a FROM bar;
对于CREATE TABLE ... SELECT
,如果给定 IF NOT EXISTS
并且目标表存在,那么不插入目的表,并且语句不会被记录。
为了确保二进制日志可以用来重建原始表,MySQL 不允许在CREATE TABLE ... SELECT
中进行并发插入。更多信息,请参见第15.1.1节,“原子数据定义语句支持”。
您不能将FOR UPDATE
作为SELECT
的一部分,用于语句例如CREATE TABLE
。如果您尝试这样做,语句将失败。new_table
SELECT ... FROM old_table
...
CREATE TABLE ... SELECT
操作将ENGINE_ATTRIBUTE
和SECONDARY_ENGINE_ATTRIBUTE
值应用于列。除非指定,否则不会将表和索引的ENGINE_ATTRIBUTE
和SECONDARY_ENGINE_ATTRIBUTE
值应用于新表。