只要结构:MySQL 写 CREATE TABLE 新表 LIKE 原表;,
结构一模一样,一行数据都不带。 本沙箱报 near "LIKE"。
本沙箱的替代是 CREATE TABLE 新表 AS SELECT * FROM 原表 WHERE 1 = 0——
1 = 0 永远不成立,所以只搬列、不搬行。
但 AS SELECT 只搬「列名和大致类型」:Demo 里 id 从 INTEGER 变 INT,
主键、非空、默认值、索引全丢了。 这是它和 like 最大的区别。
改表贵、删表险,复制表是这两件事之间的缓冲:先在副本上试、先拿快照再动手。
这一篇的重点不是「怎么复制」,是「复制过来丢了什么」。
很多人拿 AS SELECT 当正式备份,恢复回去才发现表是「裸」的。
一、只复制结构:create table ... like
CREATE TABLE 新表 LIKE 原表;——
结构一模一样,包括主键、非空、默认值、索引,但一行数据都不带。
本沙箱没有这个语法,报 near "LIKE": syntax error。 教材原码照原样记。
这个写法最典型的用途是「造一张空壳做试验」:
结构和原表完全一致,往里插数据不会踩到「列类型不对」这种问题。
第 8.1 篇说的「先在副本上试一遍」,用它造副本最干净。
二、本沙箱的替代:as select 加个假条件
CREATE TABLE copy_struct AS SELECT * FROM classes WHERE 1 = 0——
where 1 = 0 让 select 一行都不返回,于是新表只有结构、没有数据。
Demo 里 copy_struct 是 0 行。
这一招在 SQLite 里就是 like 的替代品,
几乎所有「我要一张空表但不想写一遍列定义」的场合都能用。
它和 like 有一个关键差别,看 PRAGMA 的输出就明白——下一段专门讲。
三、as select 丢了什么:这一篇最该记住的
看 copy_struct 的 PRAGMA 输出:id 的类型变成 INT(原来是 INTEGER),
pk 是 0,notnull 也全是 0。
也就是说:AS SELECT 只复制「列名和大致类型」,主键、非空、默认值、索引全丢了。
这是它和 create table … like 最大的区别。
后果是具体的:副本上可以插重复的 id(没主键了),可以插 NULL 进 name(非空没了),
查起来也慢(索引没了)。 拿它当「临时快照」够用,当「正式备份」不够——恢复回去时表是「裸」的。
四、结构加数据:直接 as select
CREATE TABLE copy_all AS SELECT * FROM classes,
三行数据都过来了。 这是「备份一张小表」最快的方式,第 7 章做备份用的就是它。
同样要记住:约束和索引没过来。 真要「备份还能原样恢复」,得走导出工具:
MySQL 是 mysqldump,它会连建表语句一起导出,恢复时先 create table 再 insert。
第 17 章讲备份。
判断标准很简单:
这份数据用来「临时看一眼」,AS SELECT 够;用来「出事时恢复」,必须用导出工具。
五、带条件复制:只搬一部分行
AS SELECT id, name, height FROM students WHERE gender = '女',
搬过来 6 行,而且只有三列。
select 里挑哪几列,新表就只有哪几列——
这也是 AS SELECT 比 like 灵活的地方。 做「给某部门导一份数据」这种活儿,一句就够。
要导成文件给别人,再套一层导出工具(SELECT … INTO OUTFILE,本沙箱没有)。
注意 INTO OUTFILE 在 MySQL 里默认只能写到服务器本机,还要开 secure_file_priv,
不是随便就能用的。
六、复制表要留意的三件事
一、自增和主键不会跟过来(用 AS SELECT 时)。
往副本里插数据,可能插出重复的 id。 真要复制自增属性,得手动重建表结构。
二、索引不会跟过来。 副本上查得慢,不代表原表慢——别拿副本的性能当结论。
反过来也成立:在副本上测出来的「索引优化效果」,回原表可能完全不同。
三、外键也不会跟过来。 副本是一张独立的表,它和原表之间没有任何关联,改副本不影响原表,
反过来也一样。 这一点在造「测试数据」时是好事,在「迁移数据」时是坑。
七、动手 Demo:三种复制:只要结构、结构加数据、带条件挑列
Demo 四段:撞一次 like,用 AS SELECT 造一张空壳,再复制一张带数据的、一张带条件挑列的。
第二段后面那个 PRAGMA 是重点——它把「丢掉的约束」一条一条摆出来了。
import _q46 as Q
c = Q.con()
# 1) 只要结构:like 是 MySQL 的,这里报错
c.execute("CREATE TABLE copy_like LIKE classes")
# 2) 本沙箱的替代:where 1 = 0
Q.run(c, "CREATE TABLE copy_struct AS SELECT * FROM classes WHERE 1 = 0")
Q.show(c, "PRAGMA table_info(copy_struct)")
# 3) 结构加数据
Q.run(c, "CREATE TABLE copy_all AS SELECT * FROM classes")
# 4) 只搬一部分行,只挑几列
Q.run(c, "CREATE TABLE copy_girl AS SELECT id, name, height "
"FROM students WHERE gender = '女'")
实跑输出:
--------------------------------------------------------------------
第四十课:复制表——只复制结构,还是连数据一起
--------------------------------------------------------------------
只复制结构:MySQL 的 create table ... like
**MySQL 写**:
`CREATE TABLE 新表 LIKE 原表;`——
**结构一模一样,包括主键、非空、默认值、索引,但一行数据都不带。**
SQL> CREATE TABLE copy_like LIKE classes
报错:near "LIKE": syntax error
**`create table … like` 是本沙箱没有的语法**,报 `near "LIKE"`。 **教材原码照原样记。**
本沙箱的等价写法:AS SELECT 加个假条件
SQL> CREATE TABLE copy_struct AS SELECT * FROM classes WHERE 1 = 0
完成
SQL> SELECT COUNT(*) AS rows_in_copy FROM copy_struct
rows_in_copy
-------------
0
(1 行)
SQL> PRAGMA table_info(copy_struct)
cid name type notnull dflt_value pk
-------------------------------------------------------------------
0 id INT 0 NULL 0
1 name TEXT 0 NULL 0
2 teacher TEXT 0 NULL 0
3 room TEXT 0 NULL 0
(4 行)
**`WHERE 1 = 0` 让 `select` 一行都不返回,于是新表只有结构、没有数据。**
**这一招在 SQLite 里就是 `like` 的替代品。**
**但它和 `like` 有一个关键差别**:看 `PRAGMA` 的输出,
**`id` 的类型变成了 `INT`,`pk` 是 0,`notnull` 也全是 0。**
**也就是说:`AS SELECT` 只复制「列名和大致类型」,主键、非空、默认值、索引全丢了。**
**这是它和 `create table … like` 最大的区别。**
结构加数据:直接 AS SELECT
SQL> CREATE TABLE copy_all AS SELECT * FROM classes
完成
SQL> SELECT id, name, teacher FROM copy_all ORDER BY id
id name teacher
------------------------
1 一班 马老师
2 二班 孔老师
3 三班 曹老师
(3 行)
**三行数据都过来了。** **这是「备份一张小表」最快的方式**,第 7 章做备份用的就是它。
**同样要记住:约束和索引没过来。** **拿它当「临时快照」够用,当「正式备份」不够**——
恢复回去时表是「裸」的。
带条件复制:只搬一部分行
SQL> CREATE TABLE copy_girl AS SELECT id, name, height FROM students WHERE gender = '女'
完成
SQL> SELECT id, name, height FROM copy_girl ORDER BY id
id name height
-----------------------
2 王芳 160.20
3 李娜 165.00
5 陈静 158.60
7 赵敏 162.80
10 吴倩 163.50
12 孙悦 161.40
(6 行)
**`select` 里挑哪几列,新表就只有哪几列**——**这也是 `AS SELECT` 比 `like` 灵活的地方。**
**做「给某部门导一份数据」这种活儿,一句就够。**
**要导成文件给别人,再套一层导出工具(`SELECT … INTO OUTFILE`,本沙箱没有)。**
复制表要留意的三件事
**一、自增和主键不会跟过来**(用 `AS SELECT` 时)。 **往副本里插数据,可能插出重复的 id。**
**二、索引不会跟过来。** 副本上查得慢,不代表原表慢——**别拿副本的性能当结论。**
**三、外键也不会跟过来。** 副本是一张独立的表,**它和原表之间没有任何关联**,
改副本不影响原表(反过来也一样)。
第一段 like 报 near "LIKE",SQLite 不认这个语法。
第二段 WHERE 1 = 0 造出 0 行的空壳,但 PRAGMA 显示 id 变 INT、pk 是 0、
notnull 全 0——约束全丢了。
第三段复制整表,三行都在:一班、二班、三班。
第四段挑列带条件,搬过来 6 个女生、只有 3 列(id/name/height)。
30 秒实验
CREATE TABLE bak AS SELECT * FROM classes之后,bak上有主键吗?怎么验证?- 要造一张「结构和
classes完全一样、但没有数据」的表,本沙箱该写什么? - 为什么不能拿
AS SELECT的副本当正式备份?
常见误区
- 把
AS SELECT的副本当正式备份。 主键、非空、索引全丢,恢复回去是一张「裸表」, 数据质量全靠运气。 - 在副本上测性能,把结论套回原表。 副本没索引,慢是必然的,不代表原表也慢。
- 以为副本和原表还连着。 复制之后就完全独立了,改一边不影响另一边,别以为在「同步」。
自测三问(点开对答案)
1. `CREATE TABLE 新表 LIKE 原表` 复制了什么?没复制什么? —— 复制完整结构(列、类型、主键、非空、默认值、索引),不复制任何数据。本沙箱不支持这个语法。 2. `AS SELECT` 复制表的时候丢了哪些东西? —— 主键、非空、默认值、索引、自增属性都丢了,只留下列名和大致类型。 3. 「临时快照」和「正式备份」的区别在哪? —— 快照只要能读、能对比,用 `AS SELECT` 就够; 正式备份要能原样恢复,必须用 `mysqldump` 这类导出工具。复制是为了留后路,下一篇是终点:8.7 删除表——最该慢一步的动作。
- ☐ (本篇完,下一篇 8.7 删除表)