Documentation Home
MySQL 8.0 参考手册  / 第 13 章 SQL 语句  / 13.1 数据定义语句  / 13.1.20 CREATE TABLE 语句  /  13.1.20.4 CREATE TABLE ... SELECT 语句

13.1.20.4 CREATE TABLE ... SELECT 语句

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包含三列的表,abc。该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)

对于 tablefoo中的每一行,都会插入一行,其中包含新列bar的值 foo和默认值。

在由 生成的表中 CREATE TABLE ... SELECT,仅在 CREATE TABLE部分中命名的列排在第一位。在这两个部分中命名的列或仅在该 SELECT部分中命名的列在其后。SELECT还可以通过在部件中指定列来覆盖列 的数据类型CREATE TABLE

如果在将数据复制到表时发生错误,则表会自动删除而不创建。但是在MySQL 8.0.21之前,使用行复制时,一条 CREATE TABLE ... SELECT语句在二进制日志中记录为两个事务,一个创建表,一个插入数据。当从二进制日志应用语句时,两个事务之间的失败或复制数据时可能导致复制空表。MySQL 8.0.21 中删除了该限制。在支持原子 DDL 的存储引擎上,CREATE TABLE ... SELECT当使用基于行的复制时,现在记录并应用为一个事务。有关详细信息,请参阅第 13.1.1 节,“原子数据定义语句支持”.

从 MySQL 8.0.21 开始,在支持原子 DDL 和外键约束的存储引擎上,当使用基于行的复制时,不允许在 CREATE TABLE ... SELECT语句中创建外键。稍后可以使用添加外键约束 ALTER TABLE

您可以在SELECTby IGNORE或之前REPLACE指示如何处理重复唯一键值的行。使用IGNORE,与唯一键值上的现有行重复的行将被丢弃。使用 REPLACE,新行替换具有相同唯一键值的行。如果既未指定IGNORE也未 REPLACE指定,则重复的唯一键值会导致错误。有关详细信息,请参阅 IGNORE 对语句执行的影响

在 MySQL 8.0.19 及之后的版本中,还可以 在;部分使用VALUES语句 。该 语句的部分必须包含一个使用 子句的表别名。要命名来自 的列 ,请使用表别名提供列别名;否则,将使用默认列名 , , , ...。 SELECTCREATE TABLE ... SELECTVALUESASVALUEScolumn_0column_1column_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    | column_0 | column_1 | column_2 |
+------+------+------+----------+----------+----------+
| 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 语句被标记为对基于语句的复制不安全。使用基于语句的模式时,此类语句会在错误日志中产生警告,并在使用模式时使用基于行的格式写入二进制日志 MIXED。另见 第 17.2.1.1 节,“基于语句和基于行的复制的优点和缺点”

CREATE TABLE ... SELECT不会自动为您创建任何索引。这样做是为了使语句尽可能灵活。如果你想在创建的表中有索引,你应该在 SELECT语句之前指定这些:

mysql> CREATE TABLE bar (UNIQUE (n)) SELECT n FROM foo;

对于CREATE TABLE ... SELECT,目标表不保留有关 selected-from 表中的列是否为生成列的信息。该 SELECT语句的一部分不能为目标表中的生成列赋值。

对于CREATE TABLE ... SELECT,目标表确实保留了原始表中的表达式默认值。

可能会发生某些数据类型转换。例如, AUTO_INCREMENT不保留属性,VARCHAR列可以成为 CHAR列。重新训练的属性是NULL(或)并且, NOT NULL对于那些具有它们的列 CHARACTER SET,,,,和 子句。 COLLATIONCOMMENTDEFAULT

使用 创建表时 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. 但是,在 MySQL 8.0.21 之前,当 CREATE TABLE ... SELECT使用基于行的复制时从二进制日志应用操作时,在复制数据时允许在复制表上进行并发插入。在支持原子 DDL 的存储引擎上,MySQL 8.0.21 中删除了该限制。有关详细信息,请参阅 第 13.1.1 节,“原子数据定义语句支持”

您不能将FOR UPDATE用作语句的一部分, SELECT例如 . 如果您尝试这样做,该语句将失败。 CREATE TABLE new_table SELECT ... FROM old_table ...

CREATE TABLE ... SELECT操作仅适用 ENGINE_ATTRIBUTESECONDARY_ENGINE_ATTRIBUTE列。除非明确指定,否则表和索引ENGINE_ATTRIBUTE以及 SECONDARY_ENGINE_ATTRIBUTE值不会应用于新表。