文档首页
MySQL 8.4 参考手册
相关文档 下载本手册
PDF (US Ltr) - 39.9Mb
PDF (A4) - 40.0Mb
手册页 (TGZ) - 258.5Kb
手册页 (Zip) - 365.5Kb
信息 (Gzip) - 4.0Mb
信息 (Zip) - 4.0Mb


MySQL 8.4 参考手册  /  ...  /  CREATE TABLE ... SELECT 语句

15.1.20.4 CREATE TABLE ... SELECT 语句

您可以通过在 CREATE TABLE 语句的末尾添加 SELECT 语句,从一个表创建另一个表。

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;

这将创建一个包含三列(a、b 和 c)的 InnoDB 表。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 部分命名的列排在前面。在两个部分中都命名或仅在 SELECT 部分命名的列排在后面。SELECT 列的数据类型可以通过在 CREATE TABLE 部分中指定该列来覆盖。

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

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

您还可以在 CREATE TABLE ... SELECT 的 SELECT 部分中使用 VALUES 语句;该语句的 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);

同样,您可以在 SELECT 的位置使用 TABLE 语句。这遵循与 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 模式时使用基于行的格式写入二进制日志。另请参阅第 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 节“原子数据定义语句支持”。

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

CREATE TABLE ... SELECT 操作仅将 ENGINE_ATTRIBUTE 和 SECONDARY_ENGINE_ATTRIBUTE 值应用于列。表和索引 ENGINE_ATTRIBUTE 和 SECONDARY_ENGINE_ATTRIBUTE 值不会应用于新表,除非显式指定。