1. 为什么本地脚本写 sqlite3,还要折腾一个统一 Key
如果你写过 Python 脚本处理本地数据,大概率用过sqlite3。它是标准库,不用装依赖,一个.db文件就是一个完整数据库,特别适合做小工具、爬虫落库、日志归档、配置缓存这类场景。但真正写起来,痛点往往不在 SQL 本身,而在两件事:一是增删改查的样板代码反复写,事务、游标、异常回滚每次都要抄一遍;二是调试阶段遇到报错,想找个 AI 辅助看看,结果每个工具的 Key、Base URL、模型名都要单独配,切来切去很烦。
这篇就围绕「Python 操作 sqlite3 增删改查」这条主线,把建表、插入、查询、更新、删除的可复制代码全部给到,同时演示怎么把 AI 辅助工具的 Base URL 统一改到 TaoToken,用一个 Key 打通脚本编写和 AI 辅助调试。适合谁?适合正在用 Python 做本地数据管理、又想让 AI 帮忙看报错和补代码的开发者。核心检索词就是 python sqlite3 增删改查,全文围绕它展开,不跑题。
先说清楚 sqlite3 的定位:它是嵌入式数据库,没有独立服务进程,你的 Python 进程直接读写文件。这意味着它没有网络开销,但也意味着并发写入能力有限,适合单机、低并发。理解这一点,后面的事务和锁问题就好解释了。
我试过在一个数据清洗脚本里用 sqlite3 存中间结果,几万条数据插入,如果不用executemany和事务,速度能差十几倍。所以下面的代码不只是「能跑」,还会带上性能相关的写法。
2. TaoToken 前置:把 AI 辅助的 Base URL 统一到一处
在写代码之前,先把 AI 辅助这一环配好。很多人卡在「AI 工具连不上」或者「每个工具都要重新填 Key」,其实只要把 Base URL 指向同一个入口就行。TaoToken 提供统一的 API 入口,官网是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api ,注意 API 地址后面不加 UTM 参数。
你需要先拿到一个 Key。进入控制台创建:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,然后在 API Keys 页面生成:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。生成后复制保存,后面配置里要用。
这里要强调一个概念:Base URL 是「请求发往哪里」,Key 是「你是谁」,Model ID 是「你要用哪个模型」。这三件套缺一不可。很多报错401就是 Key 不对,local proxy failed往往是 Base URL 写错或者网络层拦截,reading choices报错通常是返回体结构和你预期的不一致,多半是 Model ID 或接口路径不对。
如果你用的是 Claude Code 这类编码工具,配置方式是把 Base URL 指向 TaoToken 的 API 地址,Key 填刚生成的,Model ID 按文档里支持的填。文档入口:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。Claude Code 的接入说明在:https://taotoken.net/ClaudeCodeAnthropic?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
如果你用的是 Cline 或者带 MCP 的工具,同样是把 Base URL、Key、Model ID 三件套填进去。Cline 的 MCP 配置里,Base URL 用 https://taotoken.net/api ,Key 用你的,Model ID 按需选。这样你在写 sqlite3 脚本时,AI 辅助的请求都走同一个入口,不用每个工具单独维护。
对于长期做编码和 Agent 任务的,可以考虑 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。如果只是想验证某个模型对话效果,用模型对话入口:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
配好之后,你在脚本里遇到sqlite3.OperationalError之类的报错,直接贴给 AI 辅助,让它帮你分析,效率会高很多。下面进入正题,写 sqlite3 的增删改查。
3. 可复制配置:建表、插入、查询、更新、删除全流程
这一节给完整代码。先建一个person表,字段有pno(主键自增)、pname、age、score。然后依次演示插入单条、插入多条、查询单条、查询全部、更新、删除。
先看建表和连接的基础写法:
import sqlite3 # 连接数据库,文件不存在会自动创建 con = sqlite3.connect('test.db') cur = con.cursor() # 建表,IF NOT EXISTS 避免重复建表报错 create_sql = ''' CREATE TABLE IF NOT EXISTS t_person ( pno INTEGER PRIMARY KEY AUTOINCREMENT, pname TEXT NOT NULL, age INTEGER, score REAL ) ''' cur.execute(create_sql) con.commit() cur.close() con.close()注意AUTOINCREMENT配合INTEGER PRIMARY KEY才能自增。如果你只写PRIMARY KEY,SQLite 也会自增,但行为略有差异,建议显式写清楚。
插入单条数据,用参数化查询,不要用字符串拼接,避免 SQL 注入:
import sqlite3 con = sqlite3.connect('test.db') cur = con.cursor() insert_sql = 'INSERT INTO t_person(pname, age, score) VALUES(?, ?, ?)' try: cur.execute(insert_sql, ('张三', 20, 88.5)) con.commit() except Exception as e: print('插入失败:', e) con.rollback() finally: cur.close() con.close()插入多条用executemany,性能比循环execute好很多:
import sqlite3 con = sqlite3.connect('test.db') cur = con.cursor() insert_sql = 'INSERT INTO t_person(pname, age, score) VALUES(?, ?, ?)' data = [('小李', 23, 90.0), ('小花', 20, 85.5), ('小明', 18, 78.0)] try: cur.executemany(insert_sql, data) con.commit() except Exception as e: print('批量插入失败:', e) con.rollback() finally: cur.close() con.close()查询单条用fetchone,查询全部用fetchall:
import sqlite3 con = sqlite3.connect('test.db') cur = con.cursor() query_sql = 'SELECT * FROM t_person' try: cur.execute(query_sql) person = cur.fetchone() print('单条:', person) except Exception as e: print('查询失败:', e) finally: cur.close() con.close()import sqlite3 con = sqlite3.connect('test.db') cur = con.cursor() query_sql = 'SELECT * FROM t_person' try: cur.execute(query_sql) persons = cur.fetchall() for p in persons: print(p) except Exception as e: print('查询失败:', e) finally: cur.close() con.close()更新数据,注意WHERE条件一定要带,否则全表更新:
import sqlite3 con = sqlite3.connect('test.db') cur = con.cursor() update_sql = 'UPDATE t_person SET score = ? WHERE pname = ?' try: cur.execute(update_sql, (95.0, '小李')) con.commit() print('影响行数:', cur.rowcount) except Exception as e: print('更新失败:', e) con.rollback() finally: cur.close() con.close()删除数据,同样要带WHERE:
import sqlite3 con = sqlite3.connect('test.db') cur = con.cursor() delete_sql = 'DELETE FROM t_person WHERE pno = ?' try: cur.execute(delete_sql, (3,)) con.commit() print('影响行数:', cur.rowcount) except Exception as e: print('删除失败:', e) con.rollback() finally: cur.close() con.close()这里有个坑:cur.execute(delete_sql, (3,))里的(3,)是单元素元组,逗号不能省。如果你写(3),Python 会当成整数 3,不是元组,会报参数错误。这一点和某些数据库驱动不同,务必注意。
如果你想把连接管理写得更优雅,可以用with语句:
import sqlite3 with sqlite3.connect('test.db') as con: cur = con.cursor() cur.execute('SELECT * FROM t_person') print(cur.fetchall())with会在退出时自动提交或回滚,但不会自动关闭连接,这点和文件操作不同,需要留意。
4. 验证请求与成功结果:命令行和脚本双向核对
代码写完了,怎么确认数据真的写进去了?最直接的办法是用 sqlite3 命令行工具查一遍,再和 Python 脚本的输出对比。
先看命令行。在终端输入:
sqlite3 test.db进入交互模式后,执行:
.headers on .mode column SELECT * FROM t_person;你会看到类似这样的输出:
pno pname age score --- ----- --- ----- 1 张三 20 88.5 2 小李 23 95.0 3 小花 20 85.5 4 小明 18 78.0注意pno=3如果被删了,这里就不会出现。命令行验证的好处是它绕过了 Python 层,直接读文件,能确认数据确实落盘了。
再用 Python 脚本查一遍,输出应该一致:
import sqlite3 con = sqlite3.connect('test.db') cur = con.cursor() cur.execute('SELECT * FROM t_person ORDER BY pno') for row in cur.fetchall(): print(row) cur.close() con.close()如果两边输出一致,说明增删改查都正常。如果不一致,先检查是不是有未提交的事务,或者连接的是不同的.db文件。路径问题很常见,sqlite3.connect('test.db')是相对当前工作目录的,脚本和命令行的当前目录可能不同。
再验证一下更新和删除的效果。更新后重新查询,小李的score应该变成 95.0。删除后pno=3的记录应该消失。这些都可以用上面的命令行和脚本各跑一遍。
如果你在调试过程中遇到报错,比如sqlite3.OperationalError: no such table: t_person,可以把报错贴给 AI 辅助。因为前面已经把 Base URL 统一到 TaoToken,你只需要在一个地方维护 Key,不用每个工具重新配。模型对话入口可以用来快速验证:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
还有一个实用技巧:用PRAGMA table_info(t_person);查看表结构,确认字段类型和约束。命令行和 Python 里都能执行:
cur.execute('PRAGMA table_info(t_person)') for col in cur.fetchall(): print(col)输出会列出每个字段的cid、name、type、notnull、dflt_value、pk。这个在排查「为什么插入失败」时特别有用,比如NOT NULL约束没满足。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
这一节集中说报错。分两类:一类是 sqlite3 本身的,一类是 AI 辅助接入的。
sqlite3 常见错误:
sqlite3.OperationalError: no such table: t_person—— 表没建,或者连的库不对。先确认CREATE TABLE执行过,再确认.db文件路径一致。
sqlite3.IntegrityError: NOT NULL constraint failed—— 插入时某个NOT NULL字段传了None。检查数据里有没有空值。
sqlite3.ProgrammingError: Incorrect number of bindings supplied—— 参数个数和占位符不匹配。比如 SQL 里有 3 个?,你只传了 2 个值。
sqlite3.OperationalError: database is locked—— 有另一个连接没释放,或者事务没提交。确保commit或rollback后关闭游标和连接。
AI 辅助接入常见错误:
401 Unauthorized—— Key 不对或没带。检查 API Keys 页面生成的 Key 是否复制完整,有没有多余空格。重新生成入口:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
local proxy failed—— Base URL 写错,或者本地网络层有拦截。确认 Base URL 是 https://taotoken.net/api ,不要多加路径,也不要带 UTM 参数。
reading choices相关报错 —— 返回体结构和预期不符,通常是 Model ID 填错,或者接口路径不对。对照文档确认 Model ID:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
OAuth相关报错 —— 多见于 Claude Code 这类工具的登录态问题。如果你用的是 Claude Code,参考接入说明:https://taotoken.net/ClaudeCodeAnthropic?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,确认 Base URL、Key、Model ID 三件套都填对。
这里再强调一次三件套:Base URL 用 https://taotoken.net/api ,Key 用你生成的,Model ID 按文档选。任何一环错了都会报错。如果你用 Cline 的 MCP,配置里同样填这三样。
排查顺序建议:先确认 Key 有效(换个工具试),再确认 Base URL 正确(不要带多余路径),最后确认 Model ID 存在。这样能快速定位问题在哪一层。
6. 把统一 Key 用在日常脚本调试里
回到主线。你现在有了完整的 sqlite3 增删改查代码,也有了统一的 AI 辅助入口。日常写脚本时,遇到报错直接贴给 AI,让它帮你分析是 SQL 问题还是数据问题。因为 Base URL 统一到 TaoToken,你只需要维护一个 Key,不用在多个工具之间来回切换。
对于长期做编码和 Agent 任务的,Coding Plan 会更合适:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。如果只是偶尔验证模型效果,用模型对话入口就行:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
最后给一个实用建议:把 sqlite3 的连接和常用操作封装成一个小类,减少重复代码。比如:
import sqlite3 class DB: def __init__(self, path='test.db'): self.con = sqlite3.connect(path) self.cur = self.con.cursor() def execute(self, sql, params=None): try: self.cur.execute(sql, params or ()) self.con.commit() return self.cur except Exception as e: self.con.rollback() raise e def query(self, sql, params=None): self.cur.execute(sql, params or ()) return self.cur.fetchall() def close(self): self.cur.close() self.con.close()用的时候:
db = DB() db.execute('INSERT INTO t_person(pname, age, score) VALUES(?, ?, ?)', ('王五', 25, 80.0)) print(db.query('SELECT * FROM t_person')) db.close()这样你的脚本会更干净,调试时也更容易定位问题。配合统一的 AI 辅助入口,写 sqlite3 增删改查的效率会明显提升。