🐬 第19章 插入数据
📄 一句话概括

前面十八章都在读,这一章第一次往表里写。 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 秒实验

  1. 再插一行只给 cust_name,cust_id 会是几?
  2. 把第五段的三组值拆成三条 INSERT,总行数变吗?影响行数各是多少?
  3. 给 cheap 表再插一行价格 4.99 的产品,会被约束拦住吗?

常见误区

  1. 不写列名直接插值。表加了列或调了列序就整句错位,而且不报错。
  2. 以为省略的列会自动填 0 或空串。实际是 NULL 或建表时给的默认值。
  3. 一条 INSERT 只插一行还循环发一百次。多行插入快得多,批量场景必须用它。
自测三问(点开对答案) 1. INSERT 的四种写法是什么? —— 插完整行、插部分列、多行插入、插入检索出的数据(INSERT ... SELECT)。 2. 为什么推荐写全列名? —— 不写列名时值按物理列序对应,表一改结构就错位,而且不会报错。 3. 省略的列会得到什么? —— 有默认值就用默认值,没有就是 NULL;若该列 NOT NULL 又无默认值,插入直接报错。

数据进去了, 可写错的时候怎么改、怎么删? 下一章的两条语句威力最大、也最容易出事——UPDATE 与 DELETE。

相关笔记