您可以通过在 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
部分命名的列排在后面。可以通过在 CREATE TABLE
部分中也指定列来覆盖 SELECT
列的数据类型。
对于支持原子 DDL 和外键约束的存储引擎,当使用基于行的复制时,在 CREATE TABLE ... SELECT
语句中不允许创建外键。外键约束可以使用 ALTER TABLE
稍后添加。
可以在 SELECT
之前使用 IGNORE
或 REPLACE
来指示如何处理重复唯一键值的行。使用 IGNORE
时,会丢弃在唯一键值上重复现有行的行。使用 REPLACE
时,新行会替换具有相同唯一键值的行。如果既没有指定 IGNORE
也没有指定 REPLACE
,则重复唯一键值会导致错误。有关更多信息,请参见 语句执行中 IGNORE 的影响。
还可以在 SELECT
部分的 CREATE TABLE ... 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);
同样,可以使用 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
模式时会使用基于行的格式写入二进制日志。另请参见 第 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
值不会应用于新表。