您可以通过在 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
值不会应用于新表,除非显式指定。