前面十八章都在读,这一章第一次往表里写。 INSERT 只有一句,但写法有三种:给全列、给部分列、 一次插多行,另加一种「把查询结果搬进表」。 Demo 从建表开始,自增给了 3 号、默认值填了 2005-01-01、漏写的列是 NULL; 少给值当场报错,主键重复当场被拒;最后把四条便宜货整体搬成一张新表。
第 15 到 18 章都在「取」,从这一章起开始「改」。 读和写的风险完全不同:查错了只是看到错数,写错了数据就真没了。 插数据时最容易忽略的是约束(第 1 章建的 NOT NULL、主键那套), 它们在这一章开始真正发力。
一、数据插入:一次一行,还是一次多行
INSERT INTO 表 (列1, 列2) VALUES (值1, 值2) 是最基本的样子。
列名和值必须一一对应,个数也必须一样。
教材把插入分四种:插入完整的行、插入部分列、插入多行、插入检索出的数据。
四种都建立在这一句上,只是括号里的东西变了。
写数据比读数据危险:SELECT 写错只是看到错结果,INSERT 写错是真往库里塞了一条脏数据,
而且它默认立刻生效。
二、插入完整的行
最稳的写法是把列名写全:
INSERT INTO customers2 (cust_id, cust_name, cust_city, cust_email, cust_since) VALUES (...)。
列名列值的顺序也要对上。
不写列名也能跑:INSERT INTO 表 VALUES (...),值按表的物理列序填。
但这很危险——表加了一列、列序一变,整句就错位,而且不报错。
主键那列可以不填:MySQL 的 AUTO_INCREMENT 与 SQLite 的 AUTOINCREMENT 都会自己往上加。
教材的做法是在 INSERT 里给 NULL,让引擎接管。
给重复主键会被当场拒:UNIQUE constraint failed。
这不是障碍,是保护——比事后发现两条一样的记录强得多。
三、插入部分列与多行
只给一部分列,其余走默认值或 NULL:
INSERT INTO customers2 (cust_name) VALUES ('Yosemite Place')。
能省的前提是这列允许空或有默认值。
NOT NULL 又没默认值的列一定得给,不给就报错。
这条约束在建表时就定下了(第 21 章细讲),到插入的时候才显形。
多行插入是一条 INSERT 带多组值:VALUES (...), (...), (...)。
比发三次快得多,因为只解析一次、只走一趟网络。
教材专门强调这一点,它就是批量导入的标准写法。
四、插入检索出的数据
INSERT INTO 新表 (列...) SELECT ... 把查询结果整批灌进另一张表。
本课用 CREATE TABLE 新表 AS SELECT ... 一步到位:建表加搬数据。
规矩和第 17 章 UNION 一样:列数要对应、类型要兼容。
而且列名不必相同,位置对上就行(这也意味着写错位置不会报错,只搬错列)。
这是搬数据的标准手段:旧表到新表、测试库到生产库、
甚至从别的系统的导出文件先落一张临时表再灌进来。
五、小结
教材小结:INSERT 可插整行、插部分列、插多行、插检索结果,给全列名最稳,主键交给自增。 本课补三条:一是不写列名的插入怕改表;二是多行插入一次发完更快; 三是约束是当场拦,不是事后提醒。
六、动手 Demo:从空表到六行,再搬出一张新表
Demo 七段:先建表,再按四种方式各插一次,然后看自增、默认值、NULL 各给什么,最后搬一张新表、 撞一次主键。 注意 cust_id 从 1 走到 6:三行是显式给的,三行是引擎自增的。
import sqlite3
con = sqlite3.connect(":memory:")
# 建一张新表:主键交给引擎自增
con.execute("CREATE TABLE customers2 ("
"cust_id INTEGER PRIMARY KEY AUTOINCREMENT, "
"cust_name TEXT NOT NULL, cust_city TEXT, cust_email TEXT, "
"cust_since TEXT DEFAULT '2005-01-01')")
# 插入完整的行:列名一个不落,最稳
con.execute("INSERT INTO customers2 (cust_id, cust_name, cust_city, "
"cust_email, cust_since) VALUES (1, 'Coyote Inc.', 'Detroit', "
"'[email protected]', '2005-09-01')")
# 只插一部分列,省下的走默认值或 NULL
con.execute("INSERT INTO customers2 (cust_name) VALUES ('Yosemite Place')")
# 一条 INSERT 带多组值
con.execute("INSERT INTO customers2 (cust_name, cust_city) VALUES "
"('E Fudd', 'Chicago'), ('Jet Set', 'London')")
# 把检索结果直接建成表(教材版是 INSERT INTO 表 SELECT ...)
con.execute("CREATE TABLE cheap AS SELECT prod_id, prod_name, prod_price "
"FROM products WHERE prod_price <= 5")
# 主键重复会被当场拒绝
try:
con.execute("INSERT INTO customers2 (cust_id, cust_name) VALUES (1, 'x')")
except sqlite3.IntegrityError as e:
print("报错:", e)
实跑输出:
--------------------------------------------------------------------
第十九课:插入数据
--------------------------------------------------------------------
【一】先建一张新表:主键交给引擎自增
SQL> CREATE TABLE customers2 ( cust_id INTEGER PRIMARY KEY AUTOINCREMENT,
cust_name TEXT NOT NULL, cust_city TEXT, cust_email TEXT,
cust_since TEXT DEFAULT '2005-01-01')
完成
**MySQL 写 `INTEGER AUTO_INCREMENT PRIMARY KEY`,SQLite 写 `INTEGER PRIMARY KEY AUTOINCREMENT`。** 效果一样:不填就自己往上加。
**下面用的是样例库那条连接,products 还在,后面第六段要借它搬数据。**
【二】插入完整的行:列名一个不落
SQL> INSERT INTO customers2 (cust_id, cust_name, cust_city, cust_email,
cust_since) VALUES (1, 'Coyote Inc.', 'Detroit', '[email protected]', '2005-09-01')
影响 1 行
SQL> SELECT * FROM customers2
cust_id cust_name cust_city cust_email cust_since
-------------------------------------------------------------------------
1 Coyote Inc. Detroit [email protected] 2005-09-01
(1 行)
**列名列值一一对应,顺序也必须对上。** 这是最稳的写法,也是教材推荐的。
【三】不写列名会怎样:全靠位置
SQL> INSERT INTO customers2 VALUES (2, 'Mouse House', 'Columbus', NULL, '2005-09-10')
影响 1 行
**能跑,但危险**:值必须严格按表的物理列序排,**表一改列序(比如加一列在中间)就全错位。**
**少给值直接报错:table customers2 has 5 columns but 2 values were supplied**
【四】只插一部分列:省下的给默认值或 NULL
SQL> INSERT INTO customers2 (cust_name) VALUES ('Yosemite Place')
影响 1 行
SQL> SELECT * FROM customers2 WHERE cust_id = 3
cust_id cust_name cust_city cust_email cust_since
-----------------------------------------------------------------------
3 Yosemite Place NULL NULL 2005-01-01
(1 行)
**cust_id 是 3,引擎自己给的**;cust_since 拿到了建表时的默认值;
**cust_city 与 cust_email 是 NULL。**
**注意:能省的前提是这列允许为空或有默认值。**
**cust_name 加了 NOT NULL 又没默认值,就一定得给。**
【五】插入多行:一条顶三条
SQL> INSERT INTO customers2 (cust_name, cust_city) VALUES ('E Fudd', 'Chicago'), ('Jet Set',
'London'), ('ACME', 'Los Angeles')
影响 3 行
SQL> SELECT cust_id, cust_name, cust_city FROM customers2 ORDER BY cust_id
cust_id cust_name cust_city
-------------------------------------------
1 Coyote Inc. Detroit
2 Mouse House Columbus
3 Yosemite Place NULL
4 E Fudd Chicago
5 Jet Set London
6 ACME Los Angeles
(6 行)
**一条 INSERT 带三组值,比发三次快得多**(教材专门强调这一点)。
**MySQL 也支持这个写法**,而且这是它比旧版标准多做的一件事。
【六】插检索出来的数据:INSERT ... SELECT
SQL> CREATE TABLE cheap AS SELECT prod_id, prod_name,
prod_price FROM products WHERE prod_price <= 5
完成
SQL> SELECT * FROM cheap ORDER BY prod_price
prod_id prod_name prod_price
-----------------------------------------
FC Carrots 2.50
TNT1 TNT (1 stick) 2.50
FU1 Fuses 3.42
SLING Sling 4.49
(4 行)
**`CREATE TABLE 新表 AS SELECT ...` 直接把查询结果建成表**,四条便宜货全搬过来了。
**教材用的是 `INSERT INTO 表 SELECT ...`**:表要事先建好,
**列数要对应、类型要兼容**(第 17 章 UNION 那三条规则同款)。
**这一步最适合搬数据**:从一个库搬到另一个库、从旧表搬到新表,都是它。
【七】主键冲突:引擎会拦下来
**报错:UNIQUE constraint failed: customers2.cust_id**
**主键是唯一约束,插重复当场被拒**——这比事后发现两条一样的记录强得多。
第一段建表。 主键自增、名字非空、建单日期有默认值。
第二段插整行。 影响 1 行,五个值一个不落。
第三段不写列名。 能跑;少给一个值就报错,说 5 列却给了 2 个值。
第四段只给名字。 拿到 cust_id 3、默认日期 2005-01-01,城市与邮箱是 NULL。
第五段多行插入。 一条语句影响 3 行,表里从 3 行变成 6 行。
第六段搬数据。 四条便宜货成了新表 cheap 的全部内容。
第七段撞主键。 1 号已经存在,UNIQUE constraint failed 当场拦下。
30 秒实验
- 再插一行只给
cust_name,cust_id 会是几? - 把第五段的三组值拆成三条 INSERT,总行数变吗?影响行数各是多少?
- 给
cheap表再插一行价格 4.99 的产品,会被约束拦住吗?
常见误区
- 不写列名直接插值。表加了列或调了列序就整句错位,而且不报错。
- 以为省略的列会自动填 0 或空串。实际是 NULL 或建表时给的默认值。
- 一条 INSERT 只插一行还循环发一百次。多行插入快得多,批量场景必须用它。
自测三问(点开对答案)
1. INSERT 的四种写法是什么? —— 插完整行、插部分列、多行插入、插入检索出的数据(INSERT ... SELECT)。 2. 为什么推荐写全列名? —— 不写列名时值按物理列序对应,表一改结构就错位,而且不会报错。 3. 省略的列会得到什么? —— 有默认值就用默认值,没有就是 NULL;若该列 NOT NULL 又无默认值,插入直接报错。数据进去了, 可写错的时候怎么改、怎么删? 下一章的两条语句威力最大、也最容易出事——UPDATE 与 DELETE。
- ☐ (本章完,下一章 第20章 更新和删除数据)