13.1.20.4 CREATE TABLE ... SELECT Statement语句

You can create one table from another by adding a SELECT statement at the end of the CREATE TABLE statement:通过在CREATE TABLE语句末尾添加SELECT语句,可以根据一个表创建另一个表:

CREATE TABLE new_tbl [AS] SELECT * FROM orig_tbl;

MySQL creates new columns for all elements in the SELECT. MySQL为SELECT中的所有元素创建新列。For example:例如:

mysql> CREATE TABLE test (a INT NOT NULL AUTO_INCREMENT,
    ->        PRIMARY KEY (a), KEY(b))
    ->        ENGINE=InnoDB SELECT b,c FROM test2;

This creates an InnoDB table with three columns, a, b, and c. 这将创建一个包含三列(abc)的InnoDB表。The ENGINE option is part of the CREATE TABLE statement, and should not be used following the SELECT; this would result in a syntax error. ENGINE选项是CREATE TABLE语句的一部分,不应在SELECT之后使用;这将导致语法错误。The same is true for other CREATE TABLE options such as CHARSET.其他CREATE TABLE选项(如CHARSET)也是如此。

Notice that the columns from the SELECT statement are appended to the right side of the table, not overlapped onto it. 请注意,SELECT语句中的列被追加到表的右侧,而不是重叠到表的右侧。Take the following example:以以下为例:

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)

For each row in table foo, a row is inserted in bar with the values from foo and default values for the new columns.对于表foo中的每一行,将在bar中插入一行,其中包含来自foo的值和新列的默认值。

In a table resulting from CREATE TABLE ... SELECT, columns named only in the CREATE TABLE part come first. 在根据CREATE TABLE ... SELECT产生的表中,仅在CREATE TABLE部分中命名的列优先。Columns named in both parts or only in the SELECT part come after that. 在这两个部分中命名的列或仅在SELECT部分中命名的列位于该部分之后。The data type of SELECT columns can be overridden by also specifying the column in the CREATE TABLE part.还可以通过在CREATE TABLE部分中指定列来覆盖SELECT列的数据类型。

If errors occur while copying data to the table, the table is automatically dropped and not created. 如果将数据复制到表时出错,则会自动删除该表,而不会创建该表。However, prior to MySQL 8.0.21, when row-based replication is in use, a CREATE TABLE ... SELECT statement is recorded in the binary log as two transactions, one to create the table, and the other to insert data. 然而,在MySQL 8.0.21之前,当使用基于行的复制时,CREATE TABLE ... SELECT语句作为两个事务记录在二进制日志中,一个用于创建表,另一个用于插入数据。When the statement applied from the binary log, a failure between the two transactions or while copying data can result in replication of an empty table. 当从二进制日志应用该语句时,两个事务之间的故障或复制数据时的故障可能会导致复制空表。That limitation is removed in MySQL 8.0.21. MySQL 8.0.21中删除了该限制。On storage engines that support atomic DDL, CREATE TABLE ... SELECT is now recorded and applied as one transaction when row-based replication is in use. 在支持原子DDL的存储引擎上,现在在使用基于行的复制时,CREATE TABLE ... SELECT被记录并作为一个事务应用。For more information, see Section 13.1.1, “Atomic Data Definition Statement Support”.有关更多信息,请参阅第13.1.1节,“原子数据定义语句支持”

As of MySQL 8.0.21, on storage engines that support both atomic DDL and foreign key constraints, creation of foreign keys is not permitted in CREATE TABLE ... SELECT statements when row-based replication is in use. 从MySQL 8.0.21开始,在支持原子DDL和外键约束的存储引擎上,在使用基于行的复制时,CREATE TABLE ... SELECT语句中不允许创建外键。Foreign key constraints can be added later using ALTER TABLE.以后可以使用ALTER TABLE添加外键约束。

You can precede the SELECT by IGNORE or REPLACE to indicate how to handle rows that duplicate unique key values. 可以在SELECT之前加IGNOREREPLACE以指示如何处理重复唯一键值的行。With IGNORE, rows that duplicate an existing row on a unique key value are discarded. 使用IGNORE,将丢弃在唯一键值上与现有行重复的行。With REPLACE, new rows replace rows that have the same unique key value. 使用REPLACE,新行替换具有相同唯一键值的行。If neither IGNORE nor REPLACE is specified, duplicate unique key values result in an error. 如果未指定IGNOREREPLACE,则重复的唯一键值将导致错误。For more information, see The Effect of IGNORE on Statement Execution.有关更多信息,请参阅IGNORE对语句执行的影响

In MySQL 8.0.19 and later, you can also use a VALUES statement in the SELECT part of CREATE TABLE ... SELECT; the VALUES portion of the statement must include a table alias using an AS clause. 在MySQL 8.0.19及更高版本中,还可以在CREATE TABLE ... SELECTSELECT部分使用VALUES语句;语句的VALUES部分必须包含使用AS子句的表别名。To name the columns coming from VALUES, supply column aliases with the table alias; otherwise, the default column names column_0, column_1, column_2, ..., are used.要命名来自VALUES的列,请提供带有表别名的列别名;否则,将使用默认列名column_0column_1column_2……

Otherwise, naming of columns in the table thus created follows the same rules as described previously in this section. 否则,在这样创建的表中,列的命名将遵循本节前面描述的相同规则。Examples:示例:

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 |
+------+------+------+

When selecting all columns and using the default column names, you can omit SELECT *, so the statement just used to create table tv1 can also be written as shown here:选择所有列并使用默认列名时,可以省略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 |
+----------+----------+----------+

When using VALUES as the source of the SELECT, all columns are always selected into the new table, and individual columns cannot be selected as they can be when selecting from a named table; each of the following statements produces an error (ER_OPERAND_COLUMNS):当使用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);

Similarly, you can use a TABLE statement in place of the SELECT. 类似地,您可以使用TABLE语句代替SELECTThis follows the same rules as with VALUES; all columns of the source table and their names in the source table are always inserted into the new table. 这遵循与VALUES相同的规则;源表的所有列及其在源表中的名称始终插入到新表中。Examples:

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 |
+------+----+----+

Because the ordering of the rows in the underlying SELECT statements cannot always be determined, CREATE TABLE ... IGNORE SELECT and CREATE TABLE ... REPLACE SELECT statements are flagged as unsafe for statement-based replication. 由于无法始终确定基础SELECT语句中行的顺序,CREATE TABLE ... IGNORE SELECTCREATE TABLE ... REPLACE SELECT语句对于基于语句的复制被标记为不安全。Such statements produce a warning in the error log when using statement-based mode and are written to the binary log using the row-based format when using MIXED mode. 使用基于语句的模式时,此类语句会在错误日志中生成警告,使用MIXED模式时,此类语句会使用基于行的格式写入二进制日志。See also Section 17.2.1.1, “Advantages and Disadvantages of Statement-Based and Row-Based Replication”.另请参阅第17.2.1.1节,“基于语句和基于行的复制的优缺点”

CREATE TABLE ... SELECT does not automatically create any indexes for you. CREATE TABLE ... SELECT不会自动为您创建任何索引。This is done intentionally to make the statement as flexible as possible. 这样做是为了使声明尽可能灵活。If you want to have indexes in the created table, you should specify these before the SELECT statement:如果希望在创建的表中具有索引,则应在SELECT语句之前指定这些索引:

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

For CREATE TABLE ... SELECT, the destination table does not preserve information about whether columns in the selected-from table are generated columns. 对于CREATE TABLE ... SELECT,目标表不会保留有关“从中选择”表中的列是否为生成列的信息。The SELECT part of the statement cannot assign values to generated columns in the destination table.语句的SELECT部分无法为目标表中生成的列赋值。

For CREATE TABLE ... SELECT, the destination table does preserve expression default values from the original table.对于CREATE TABLE ... SELECT,目标表将保留原始表中的表达式默认值。

Some conversion of data types might occur. 可能会发生某些数据类型转换。For example, the AUTO_INCREMENT attribute is not preserved, and VARCHAR columns can become CHAR columns. 例如,AUTO_INCREMENT属性不保留,VARCHAR列可以变成CHAR列。Retrained attributes are NULL (or NOT NULL) and, for those columns that have them, CHARACTER SET, COLLATION, COMMENT, and the DEFAULT clause.重新训练的属性为NULL(或NOT NULL),对于包含这些属性的列,包括CHARACTER SETCOLLATIONCOMMENTDEFAULT子句。

When creating a table with CREATE TABLE ... SELECT, make sure to alias any function calls or expressions in the query. 使用CREATE TABLE ... SELECT创建表时,请确保为查询中的任何函数调用或表达式添加别名。If you do not, the CREATE statement might fail or result in undesirable column names.否则,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;

You can also explicitly specify the data type for a column in the created table:还可以在创建的表中显式指定列的数据类型:

CREATE TABLE foo (a TINYINT NOT NULL) SELECT b+1 AS a FROM bar;

For CREATE TABLE ... SELECT, if IF NOT EXISTS is given and the target table exists, nothing is inserted into the destination table, and the statement is not logged.对于CREATE TABLE ... SELECT,如果给定IF NOT EXISTS且目标表存在,则不向目标表中插入任何内容,并且不记录该语句。

To ensure that the binary log can be used to re-create the original tables, MySQL does not permit concurrent inserts during CREATE TABLE ... SELECT. 为了确保二进制日志可以用于重新创建原始表,MySQL不允许在CREATE TABLE ... SELECT期间并发插入。However, prior to MySQL 8.0.21, when a CREATE TABLE ... SELECT operation is applied from the binary log when row-based replication is in use, concurrent inserts are permitted on the replicated table while copying data. 但是,在MySQL 8.0.21之前,当从二进制日志中应用CREATE TABLE ... SELECT操作时,当使用基于行的复制时,复制数据时允许在复制的表上进行并发插入。That limitation is removed in MySQL 8.0.21 on storage engines that support atomic DDL. MySQL 8.0.21在支持原子DDL的存储引擎上消除了这一限制。For more information, see Section 13.1.1, “Atomic Data Definition Statement Support”.有关更多信息,请参阅第13.1.1节,“原子数据定义语句支持”

You cannot use FOR UPDATE as part of the SELECT in a statement such as CREATE TABLE new_table SELECT ... FROM old_table .... 在诸如CREATE TABLE new_table SELECT ... FROM old_table ...这样的语句中,你不能将FOR UPDATE用作SELECT的一部分。If you attempt to do so, the statement fails.如果尝试执行此操作,则语句将失败。

CREATE TABLE ... SELECT operations apply ENGINE_ATTRIBUTE and SECONDARY_ENGINE_ATTRIBUTE values to columns only. CREATE TABLE ... SELECT操作只将ENGINE_ATTRIBUTE值和SECONDARY_ENGINE_ATTRIBUTE值应用于列。Table and index ENGINE_ATTRIBUTE and SECONDARY_ENGINE_ATTRIBUTE values are not applied to the new table unless specified explicitly.表和索引ENGINE_ATTRIBUTE值和SECONDARY_ENGINE_ATTRIBUTE值没有应用于新表,除非显式指定。