🐦 第7章 扣子数据库实战
📄 一句话概括

三步:建库、建表、定字段。 前两步点两下,第三步要动脑。 字段类型在平台上是硬约束:整数位填字母,写入时就报错。 这是好事——错在写的时候比错在读的时候好查。 最容易漏的是自增主键。它不为了唯一,为了「改数据时能指到那一行」。

📝 本章和前面几章的关系

上一节讲它是什么,这一节讲怎么建。 建表这一步决定了后面所有事:字段定少了以后要补,定多了永远空着,没有主键就等着改错数据。 这一节的 Demo 用 sqlite3 的宽松来反衬平台的严格——同一个错误,两边反应完全不同。

一、建库:名字和用途

建库只要两样:名字和用途说明。名字用英文或拼音,别用中文——有些节点的 SQL 里要拼表名。 用途说明是给谁看的?给人看的,也是给智能体看的。挂到智能体上时,它靠这段说明判断该不该碰这个库。 所以说明要写成「用户会怎么说」:「登记来访人员,记录姓名、单位、事由与时间」, 比「访客表」有用得多。这一条第 3.1 节讲过一遍。

二、建表:字段怎么定

字段是这张表的全部。定之前先列三样东西。 一、这条记录最少要有哪几个字段才不废? 那几个设为必填,其余可空。 二、哪些字段将来要用来查? 那些字段的写法要统一(日期统一格式、单位用下拉)。

三、哪些字段其实属于另一件事? 那些该拆走,别硬塞进来。 三样列完,字段表就出来了。 演示里访客表定了五个字段:姓名(必填)、电话、来访人数、是否 VIP、到访时间。

三、字段类型:平台严,这里松

演示里试了四种插法:正常插入、整数位塞字母、必填位填空、布尔位塞 2。

试什么 sqlite 的反应 平台的反应
正常插入 收下 收下
整数位塞字母 收下,类型变 text 写入时报错
必填位填空 报 NOT NULL 写入时报错
布尔位塞 2 收下 写入时报错

三种「收下」里,第一种和第三种最坑:它悄悄改了类型,之后读出来是另一种东西。 平台严一点是好事。错在写入那一步,你能看到报错;错在读出来时,你看到的是一句缺了角的答复。

四、为什么一定要自增主键

很多人不加主键,觉得「姓名够用了」。不够。 演示里那一步:要改张伟那一行,没有主键只能写 WHERE name = '张伟'。两个张伟,就一起改了。 有 gid,写 WHERE gid = 1,改一个是一个。主键的价值在改和删,不在查。

这一条在下一节会变成一个真事故——忘写 WHERE 那次,五行全被覆盖。加个主键,至少能精确补回来。

五、定多了和定少了

定多了:空字段永远空着,占位置,还让下游提示词里多出几个「未知」。 定少了:以后要加。 加字段本身不难(一句 ALTER TABLE 或点一下),难的是已经写进去的历史记录,新字段是空的, 那批数据要补。 所以定字段时多问一句:这个字段三个月后还会用吗? 不会就别加。

反过来也有一条: 记录之间要对比的字段,一定要单独存。把「三点半」和「来访人数三个」混在一个文本字段里, 以后统计就做不了。

六、建表的三条规矩

一、至少一个必填字段。 允许全空的表,早晚会被插进一堆空行。 二、加自增主键。 不为了唯一,为了能精确指到一行。 三、日期用日期类型,别用文本。 文本日期排序会出错——「2026-9-1」排在「2026-10-1」后面。

三条都是「现在多做一步,以后少一次事故」。

七、动手 Demo:建库、建表、定字段

建一张访客表,五个字段。然后故意插四种错数据,看 sqlite 收不收。 再看收下来的那些,读出来变成了什么。

CREATE TABLE guest (
  gid INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL,
  phone TEXT,
  count INTEGER,
  vip BOOLEAN,
  come_at DATETIME)

INSERT INTO guest (name, count) VALUES ('李四', 'abc')   -- 整数位塞字母
INSERT INTO guest (name) VALUES (NULL)                  -- 必填位填空
INSERT INTO guest (name, vip) VALUES ('王五', 2)        -- 布尔位塞 2

实跑输出:

--------------------------------------------------------------------
建库、建表、定字段
--------------------------------------------------------------------


  ① 四种字段类型,各试一遍
  --------------------------------------------------------------------
  正常插入:收下了
  整数位塞字母:收下了
  必填位填空:被拦,NOT NULL constraint failed: guest.name
  布尔位塞 2:收下了

  ② 收下来的那些,读出来是什么
  --------------------------------------------------------------------

| gid | name | count | count 的类型 | vip | vip 的类型 |
|---:|---|---:|---|---:|---|
| 1 | 张伟 | 3 | integer | 1 | integer |
| 2 | 李四 | abc | text | None | null |
| 3 | 王五 | None | null | 2 | integer |

  「abc」塞进整数位,sqlite 收了,类型变成 text。「2」塞进布尔位,也收了。
  这就是宽松:类型只是建议,不是约束。

  ③ 平台那一侧会怎么处理
  --------------------------------------------------------------------
  平台数据库的字段类型只有几种:文本、整数、数字、布尔、日期。
  整数位填「abc」,平台在写入时就报错,不会存进去。
  这一步是好事:错在写的时候,比错在读的时候好查。
  真正插进一行,看它长什么样:
    (1, '张伟', '13900000001', 3, 1, '2026-10-30 09:00')

  ④ 字段定多了和定少了
  --------------------------------------------------------------------
  定多了:空字段永远空着,占位置,还让下游的提示词里多出几个「未知」。
  定少了:以后要加。
  加字段在 sqlite 里是一句 ALTER TABLE,在平台上也是点一下——但已经写进去的历史记录,新字段是空的,
  之前那批数据要补。
  所以定字段的时候多问一句:这个字段三个月后还会用吗? 不会就别加。

  ⑤ 自增主键该不该要
  --------------------------------------------------------------------
  要。理由不是「唯一」,是改数据的时候需要它。
  没有主键,你要改张伟那一行,只能写 WHERE name = '张伟'——两个张伟就一起改了。
  有 gid,写 WHERE gid = 1,改一个是一个。
  这一条在第 7.3 节会变成一个真事故。

①第一段四种插法。只有「必填位填空」被拦,报 NOT NULL;另外两种都收下了。 ②第二段最关键:收下来的那些,typeof 一看就露馅。 「abc」塞进整数位后类型变成 text,「2」塞进布尔位还是 integer。 ③所以宽松不是好事:类型只是建议,不是约束。 错在写入时你能看见,错在读出来时你看不见。 ④平台那一侧会在写入时就报错。这一步是好事——错在写的时候比错在读的时候好查。 ⑤最后那段讲主键:它的价值在改和删,不在查。没有主键,改张伟那一行要靠姓名,两个张伟就一起改了。

30 秒实验

  1. 数一下你那张表有几个字段,其中几个是必填的。一个必填都没有的,早晚会进空行。
  2. 检查你的日期字段类型。是文本的话,试一下按它排序,看顺序对不对。
  3. 给你所有的表补一个自增主键。补完想想上次改数据是怎么定位那一行的。

常见误区

  1. 「类型不重要,反正是文本存。」整数位存了字母,读出来算数就会崩。平台会在写入时拦,别绕过。
  2. 「姓名够唯一了,不用主键。」两个同名的人就一起被改。主键是为了精确指到一行。
  3. 「字段先都加上,以后用得上。」空字段永远空着,还会让下游提示词里多几个「未知」。
自测三问(点开对答案) 1. 建表前要列哪三样东西? —— 最少要哪几个字段才不废、哪些字段将来要查、哪些字段其实属于另一件事。 2. 为什么说「类型宽松」不是好事? —— 它会悄悄改类型,读出来是另一种东西。错在写入时能看见,错在读出来时看不见。 3. 自增主键真正的用途是什么? —— 改和删的时候能精确指到一行。不为了唯一,为了不误伤同名的行。

表建好了,下一节往里写数据。四种操作各有一个坑,其中一个能让整张表在一秒钟内被覆盖。

相关笔记