SQLite 向现有表添加新列:用 TaoToken 统一 Key 跑通迁移脚本与配置骨架
1. 为什么给 SQLite 老表加列总踩坑SQLite 是很多小工具、桌面应用、边缘脚本的默认数据库文件即库、零运维改起来却不像 MySQL 那样随手ALTER TABLE。当你面对一张已经跑了几个月、存了几万行数据的表想加一个freq、status、created_at这类字段时最容易撞上的就是duplicate column name、Cannot add a NOT NULL column with default value NULL、以及迁移脚本执行到一半中断导致结构不一致。这篇聚焦一个具体场景SQLite 已有表结构变更用ALTER TABLE ADD COLUMN一次性加列且不破坏存量数据。我会把可复制的迁移 SQL、config.toml与settings.json配置骨架、以及用 TaoToken 统一 Key 调用 API 执行迁移与回滚的验证动作串起来。适合正在写 Python 脚本、做本地数据工具、或者要给 Agent 加持久化能力的开发者。核心检索词就三个SQLite、数据库、添加新列。SQLite 的ALTER TABLE能力比多数人想象得窄。它支持ADD COLUMN、RENAME COLUMN、DROP COLUMN3.35.0、RENAME TO但不支持修改列类型、不支持加约束后回填。所以加列这件事语法简单约束却不少下面逐条拆。2. TaoToken 前置统一 Key 与配置骨架迁移脚本本身不需要联网但我想让「执行迁移 → 调用模型校验结果 → 记录回滚点」这条链路可复用于是用 TaoToken 做统一 Key 管理。它的作用是一个 Key 走通模型对话、Coding Plan、API 调用不用在多个平台之间来回切配置。官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 基址不加 UTMhttps://taotoken.net/api先建 Key再写配置。控制台地址https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteKey 管理页https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite模型对话页验证模型是否通https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite长期编码或 Agent 场景用 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewriteClaude Code 接入参考https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite注意Key 只放环境变量或本地配置文件别硬编码进迁移脚本提交到仓库。2.1 config.toml 骨架# config.toml [app] name sqlite-migrator db_path ./data/tree.db backup_dir ./data/backup [taotoken] base_url https://taotoken.net/api api_key ${TAOTOKEN_API_KEY} model claude-sonnet timeout 60 [migration] table summary column freq column_type INTEGER default_value 0 not_null true2.2 settings.json 骨架{ app: { db_path: ./data/tree.db, backup_dir: ./data/backup }, taotoken: { base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, model: claude-sonnet }, migration: { table: summary, column: freq, column_type: INTEGER, default_value: 0, not_null: true, rollback_snapshot: true } }两份配置语义一致toml适合 Python 的tomllibjson适合 Node 或跨语言脚本。api_key用${TAOTOKEN_API_KEY}占位运行时从环境变量注入。3. 可复制配置ALTER TABLE ADD COLUMN 的语法约束SQLite 加列有三条硬约束绕不过去第一不能加NOT NULL且无默认值的列。存量行没有值SQLite 直接报Cannot add a NOT NULL column with default value NULL。解法是给DEFAULT。第二默认值必须是常量。DEFAULT CURRENT_TIMESTAMP在ADD COLUMN里不被接受只能用字面量如0、、1970-01-01。第三加列是 O(1) 元数据操作不重写表所以大表也很快但加完列后旧行的新列值就是默认值。3.1 基础加列 SQL-- 给 summary 表加 freq 列默认 0非空 ALTER TABLE summary ADD COLUMN freq INTEGER NOT NULL DEFAULT 0;3.2 带存在性判断的 Python 迁移脚本import sqlite3 import os import shutil from datetime import datetime DB_PATH ./data/tree.db TABLE summary COLUMN freq COLUMN_TYPE INTEGER DEFAULT_VALUE 0 def column_exists(cursor, table, column): cursor.execute(fPRAGMA table_info({table})) return any(row[1] column for row in cursor.fetchall()) def backup_db(): os.makedirs(./data/backup, exist_okTrue) ts datetime.now().strftime(%Y%m%d_%H%M%S) dst f./data/backup/tree_{ts}.db shutil.copy2(DB_PATH, dst) return dst def migrate(): backup backup_db() print(f[backup] {backup}) conn sqlite3.connect(DB_PATH) cur conn.cursor() try: if column_exists(cur, TABLE, COLUMN): print(f[skip] {TABLE}.{COLUMN} 已存在) return sql fALTER TABLE {TABLE} ADD COLUMN {COLUMN} {COLUMN_TYPE} NOT NULL DEFAULT {DEFAULT_VALUE} cur.execute(sql) conn.commit() print(f[ok] 已添加 {TABLE}.{COLUMN}) except sqlite3.OperationalError as e: print(f[error] {e}) conn.rollback() finally: conn.close() if __name__ __main__: migrate()PRAGMA table_info是判断列是否存在的标准做法比try/except更干净。备份用文件拷贝SQLite 单文件直接shutil.copy2即可注意拷贝前确保没有写事务在跑。3.3 回滚 SQLSQLite 3.35.0 支持DROP COLUMNALTER TABLE summary DROP COLUMN freq;低于 3.35.0 的版本只能走「建新表 → 拷数据 → 删旧表 → 改名」四步或者直接用备份文件还原。生产环境建议先查版本SELECT sqlite_version();4. 验证请求与成功结果迁移执行完要验证三件事列存在、存量数据没丢、新列默认值正确。4.1 结构验证import sqlite3 conn sqlite3.connect(./data/tree.db) cur conn.cursor() cur.execute(PRAGMA table_info(summary)) for row in cur.fetchall(): print(row) conn.close()输出里应能看到(序号, freq, INTEGER, 1, 0, 0)其中第 4 位1表示NOT NULL第 5 位0是默认值。4.2 数据验证SELECT COUNT(*) FROM summary; SELECT COUNT(*) FROM summary WHERE freq 0;两条结果应相等说明存量行都拿到了默认值。4.3 用 TaoToken 统一 Key 做模型侧校验迁移完我想让模型读一遍表结构和样本数据确认没有异常。用统一 Key 调 APIimport os import requests API_KEY os.environ[TAOTOKEN_API_KEY] BASE_URL https://taotoken.net/api def ask_model(prompt): resp requests.post( f{BASE_URL}/v1/messages, headers{ Authorization: fBearer {API_KEY}, Content-Type: application/json, }, json{ model: claude-sonnet, max_tokens: 512, messages: [{role: user, content: prompt}], }, timeout60, ) resp.raise_for_status() return resp.json() if __name__ __main__: schema summary(id INTEGER, name TEXT, freq INTEGER NOT NULL DEFAULT 0) sample id1,nameroot,freq0 print(ask_model(f表结构{schema}\n样本{sample}\n请判断加列迁移是否合理一句话。))成功时返回 JSONcontent字段里有模型回复。如果返回 401检查 Key返回 404检查base_url是否漏了/v1超时就把timeout调到 120。5. 本篇常见错排查报错一duplicate column name: freq列已存在。用PRAGMA table_info先判断别裸跑ALTER。上面脚本里的column_exists就是干这个的。报错二Cannot add a NOT NULL column with default value NULL加了NOT NULL却没给DEFAULT。补上DEFAULT 0或DEFAULT 。报错三near CURRENT_TIMESTAMP: syntax errorADD COLUMN的默认值只能是常量。想要时间戳先加列给常量默认值再用UPDATE回填ALTER TABLE summary ADD COLUMN created_at TEXT NOT NULL DEFAULT ; UPDATE summary SET created_at datetime(now) WHERE created_at ;报错四迁移后旧行新列为 NULL说明建列时没写NOT NULL DEFAULTSQLite 给旧行填了 NULL。要么重建列要么UPDATE回填。报错五database is locked有别的连接在写。迁移前关掉其他进程或设置PRAGMA busy_timeout 5000;。报错六TaoToken 调用返回 401/403Key 没注入环境变量或复制时带了空格。用echo $TAOTOKEN_API_KEY确认。报错七回滚时DROP COLUMN报语法错SQLite 版本低于 3.35.0。查SELECT sqlite_version();低版本走备份还原。6. 把迁移链路固定下来加列这件事语法一行坑在约束和验证。我的做法是迁移前必备份迁移中用PRAGMA table_info做幂等判断迁移后用COUNT对比存量行最后用 TaoToken 统一 Key 调一次模型做结构合理性检查。这样一套跑下来加列不会破坏存量数据回滚也有据可依。如果你要把这套逻辑接进长期跑的 Agent 或编码工作流Coding Plan 那条链路更省心https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite模型侧验证走这里https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewriteKey 和接入文档分别是https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 和 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite下次再遇到 SQLite 加列直接把这套脚本和配置骨架复制过去改table、column、column_type三个字段就能跑。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →