
1. 从一次本地数据翻车说起sqlite 增删改查到底难在哪刚接触数据库那会儿我总觉得 sqlite 是个玩具——一个文件、零配置、不用装服务随手import sqlite3就能跑。直到有次写了个小工具把用户填的表单存进data.db结果第二天打开发现数据全没了才意识到问题不在 sqlite 本身而在我对 cursor 游标、事务提交、占位符传参这几件事的理解全是模糊的。sqlite 是一款轻量级嵌入式数据库整个数据库就是磁盘上的一个文件源码约三万行 C 代码占用资源极低处理速度在小型数据场景下甚至比 MySQL、PostgreSQL 更快。它适合谁适合刚学数据库的开发者、写本地小工具的人、做嵌入式或桌面应用的人以及想在不搭服务的前提下练手 SQL 的人。你能用它做什么建表、插入、查询、更新、删除也就是常说的增删改查CRUD再加上 cursor 游标来逐条或批量取结果。但小白最容易踩的坑集中在三处第一execute执行完不commit程序一关数据就回滚第二占位符?传参时把参数写成了字符串拼接既报错又有注入风险第三fetchone、fetchmany、fetchall混用不知道游标位置是接着上次往下走的。这篇笔记就围绕 sqlite 增删改查全流程和 cursor 方法实操展开每一步都给可直接复制的 SQL 与 Python 代码最后再讲怎么把 AI 工具的 Base URL 统一改到 TaoToken用一个 Key 管理所有模型调用省得每个工具配一遍密钥。我试过把建表、插入、查询、更新、删除拆成独立脚本跑一遍再对照执行结果看游标行为理解速度比只看文档快得多。下面按这个顺序来。2. 前置准备TaoToken 统一 Key 与 sqlite 环境搭好在写增删改查之前先把两件事准备好本地 sqlite 环境以及 AI 工具的 Key 统一管理。前者是 Python 自带的后者是为了让你在写代码时能顺手让 AI 帮你补 SQL、查报错而不用在多个平台之间来回切换密钥。sqlite 环境几乎零成本。Python 3 内置sqlite3模块不需要pip install任何东西。你只要确认 Python 版本python --version # 期望输出类似 Python 3.10.x 或更高然后验证 sqlite3 模块可用python -c import sqlite3; print(sqlite3.sqlite_version) # 期望输出类似 3.39.5如果这两条都正常环境就齐了。数据库文件会在你第一次connect时自动创建不用手动建库。接下来是 TaoToken 的前置配置。TaoToken 是一个统一 API 入口官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 。它的作用是让你用同一个 Key 去调用不同模型写代码时遇到 SQL 报错、cursor 行为不理解可以直接在对话里问不用每个工具单独配密钥。你需要先拿到 Key。进入控制台创建 API Key地址是 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentsqlite_crud_cursor 。创建后复制那串以sk-开头的密钥妥善保存页面关闭后通常不再完整显示。拿到 Key 后把它配到你的 AI 编码工具里。以常见的 OpenAI 兼容配置为例Base URL 填https://taotoken.net/apiAPI Key 填你刚复制的那串Model ID 按你实际要用的模型填。如果你用的是 Claude Code 这类工具配置项名称可能不同但三件套不变Base URL、Key、Model ID。这三样缺一不可尤其是 Model ID填错会直接报模型不存在。这里要提醒一句TaoToken 是统一 API 入口不是让你替代编辑器或数据库工具它解决的是多个 AI 工具密钥分散的问题。sqlite 的增删改查还是在你本地 Python 里跑两者互不干扰。配置完成后你可以先在模型对话页面发一条测试消息确认 Key 有效。模型对话入口是 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentsqlite_crud_cursor 。如果返回正常说明前置准备完成可以进入代码环节了。3. 可复制配置建表、增删改查与 cursor 方法全代码这一节是核心所有代码都可以直接复制运行。我按建表 → 插入 → 查询 → 更新 → 删除的顺序来每一步都说明游标在做什么。先建一个统一的脚本骨架。数据库文件叫demo.db表叫user字段是id主键和nameimport sqlite3 # 连接数据库文件不存在会自动创建 conn sqlite3.connect(demo.db) cur conn.cursor() # 建表IF NOT EXISTS 避免重复建表报错 cur.execute( CREATE TABLE IF NOT EXISTS user ( id INTEGER PRIMARY KEY, name TEXT NOT NULL ) ) conn.commit() cur.close() conn.close() print(建表完成)运行后你会看到建表完成同目录下多了一个demo.db文件。这里id INTEGER PRIMARY KEY表示主键插入重复 id 会报错这点后面排错会用到。插入数据分单条和多条。单条用execute多条用executemany占位符统一用?参数用元组或列表传不要用字符串拼接import sqlite3 conn sqlite3.connect(demo.db) cur conn.cursor() sql INSERT INTO user (id, name) VALUES (?, ?) # 插入一条 cur.execute(sql, (0, lily)) # 插入多条用列表保存 data [(1, li), (2, davy), (3, july)] cur.executemany(sql, data) cur.close() conn.commit() # 关键不 commit 数据不会真正写入 conn.close() print(插入完成)注意conn.commit()的位置在cur.close()之后、conn.close()之前。查询不需要 commit只有改变表内容增、改、删才需要。查询是 cursor 方法体现最明显的地方。fetchone取一条fetchmany(n)取 n 条fetchall取剩余全部而且游标位置是连续的——下一次取从上次结束的地方开始import sqlite3 conn sqlite3.connect(demo.db) cur conn.cursor() sql SELECT * FROM user WHERE id 0 cur.execute(sql) result1 cur.fetchone() # 取第 1 条 result2 cur.fetchone() # 取第 2 条 result3 cur.fetchmany(3) # 从第 3 条开始取 3 条 result4 cur.fetchall() # 取剩余全部 print(查找一条, result1) print(查找一条, result2) print(查找3条, result3) print(查找所有, result4) cur.close() conn.close()按前面插入的数据输出应该是查找一条 (0, lily) 查找一条 (1, li) 查找3条 [(2, davy), (3, july)] 查找所有 []最后fetchall返回空列表因为四条数据已经被前面取完了。这就是游标接着上次走的直观体现很多人第一次看到[]会以为查询失败其实是游标到底了。更新数据用UPDATE ... SET ... WHERE ...同样支持单条和多条import sqlite3 conn sqlite3.connect(demo.db) cur conn.cursor() sql UPDATE user SET name ? WHERE id ? # 单条更新 cur.execute(sql, (lily_update, 0)) # 多条更新 data [(update1, 1), (update2, 2)] cur.executemany(sql, data) cur.close() conn.commit() conn.close() print(更新完成)删除用DELETE FROM ... WHERE ...注意WHERE条件别写错否则会删全表import sqlite3 conn sqlite3.connect(demo.db) cur conn.cursor() sql DELETE FROM user WHERE id ? cur.execute(sql, (1,)) # 单元素元组要带逗号 cur.execute(SELECT * FROM user) result cur.fetchall() print(删除后剩余, result) cur.close() conn.commit() conn.close()执行后剩余[(0, lily_update)]因为 id 大于等于 1 的都被删了。这里(1,)的单元素元组逗号不能省写成(1)会被当成整数导致参数数量不匹配报错。如果你用 AI 工具辅助写这些 SQL把 Base URL 配到 TaoToken 后可以直接在对话里贴报错让它分析。比如 cursor 取数顺序不对、commit 漏了都能快速定位。需要长期做编码和 Agent 任务的可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentsqlite_crud_cursor 。4. 验证请求跑一遍看执行结果与成功标志代码写完不算完得跑一遍确认结果符合预期。这一节把完整流程串起来给你一个可对照的验证清单。第一步确认数据库文件生成。运行建表脚本后用命令行查看ls -lh demo.db # 期望看到 demo.db 文件大小几 KB 到几十 KB第二步插入后验证数据条数。可以用 Python 快速查import sqlite3 conn sqlite3.connect(demo.db) cur conn.cursor() cur.execute(SELECT COUNT(*) FROM user) print(当前记录数, cur.fetchone()) cur.close() conn.close()插入四条后应输出当前记录数 (4,)。第三步验证 cursor 取数顺序。把第 3 节的查询脚本原样跑一遍对照输出查找一条 (0, lily) 查找一条 (1, li) 查找3条 [(2, davy), (3, july)] 查找所有 []如果fetchmany(3)返回的不是从第 3 条开始而是又从第 1 条开始说明你在中间重新execute了查询游标被重置了。这是常见误解每次execute都会重新定位游标到结果集开头。第四步验证更新生效。更新后查询 id 为 0 的记录cur.execute(SELECT * FROM user WHERE id 0) print(cur.fetchone()) # 期望 (0, lily_update)第五步验证删除范围。删除 id 1 后全表查询应只剩 id 为 0 的记录。如果还剩其他记录检查WHERE条件是否写成了id 1或漏了参数。第六步验证事务提交。这是最容易翻车的一步。故意写一个不 commit 的脚本import sqlite3 conn sqlite3.connect(demo.db) cur conn.cursor() cur.execute(INSERT INTO user (id, name) VALUES (?, ?), (99, no_commit)) cur.close() conn.close() # 没有 commit然后重新连接查询 id 为 99 的记录你会发现查不到。这就是事务未提交导致数据回滚。把conn.commit()补上再跑就能查到。这个验证能帮你彻底记住 commit 的作用。如果你在验证过程中让 AI 帮你分析输出记得 Base URL 用https://taotoken.net/apiKey 用控制台创建的那串Model ID 填对。三件套齐了AI 才能正确读到你的报错上下文。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentsqlite_crud_cursor 遇到配置问题可以对照查。5. 本篇常见错排查401、占位符、游标到底、commit 漏写跑 sqlite 增删改查时报错大多集中在几类。这一节按真实报错对照排查顺便把 AI 工具配置的坑一起讲了。第一类sqlite3.OperationalError: no such table: user。原因是你还没建表就插入或查询。解决先跑建表脚本确认demo.db里表存在。可以用SELECT name FROM sqlite_master WHERE typetable查看所有表。第二类sqlite3.IntegrityError: UNIQUE constraint failed: user.id。主键重复。插入时 id 具有唯一性不能插相同 id。解决换一个没用过的 id或者把主键改成INTEGER PRIMARY KEY AUTOINCREMENT让数据库自增。第三类sqlite3.ProgrammingError: Incorrect number of bindings supplied。占位符数量和参数数量不匹配。常见于execute(sql, (1))这种写法单元素元组漏了逗号。解决写成(1,)。另外executemany的第二个参数必须是列表或元组的序列不能是单个元组。第四类查询返回空列表[]。不一定是没数据很可能是游标已经到底。比如你先fetchall再fetchone第二次必然为空。解决需要重新取数就重新execute一次查询游标会重置到开头。第五类数据插入后查不到。九成是漏了conn.commit()。sqlite 默认开启事务增删改不提交不会落盘。解决在cur.close()后、conn.close()前加conn.commit()。查询不需要 commit。第六类AI 工具报401 Unauthorized。这是 Key 无效或没带上。检查 Base URL 是否为https://taotoken.net/apiKey 是否完整复制以sk-开头有没有多余空格。如果用的是 Claude Code 类工具确认配置项名称对应正确三件套 Base URL、Key、Model ID 都要填。第七类报local proxy failed或连接超时。先确认网络能正常访问 API 端点再检查 Base URL 有没有多写或少写路径。TaoToken 的 API 端点是https://taotoken.net/api不要自己拼/v1之类的后缀除非文档明确要求。第八类报reading choices相关错误。通常是返回结构和你工具预期的格式不一致多半是 Model ID 填错或者用了不支持的模型名。解决核对 Model ID 拼写确认该模型在你的套餐内可用。第九类OAuth 相关报错。如果你用的是需要 OAuth 授权的工具确认授权流程走完Token 没过期。这类问题在接入文档里有说明对照检查即可。第十类cursor对象关闭后继续使用报ProgrammingError: Cannot operate on a closed cursor。解决cur.close()之后不要再调execute或fetch需要继续操作就重新conn.cursor()。把这几类对照一遍基本能覆盖新手 90% 的报错。遇到没列出的把完整报错贴到 AI 对话里让它结合你的代码分析比盲猜快得多。6. 把 Key 统一到 TaoToken后续编码与 Agent 的接入方式sqlite 的增删改查跑通后你可能会想把它接进更大的项目比如写个带 AI 辅助的数据管理工具或者让 Agent 自动生成 SQL 并执行。这时候密钥管理就成了问题——不同工具、不同模型各配一套 Key改起来很烦。TaoToken 的价值就在这里一个 Key 管所有模型调用。具体接入方式分两种场景。第一种是模型对话和临时问答直接用模型对话页面Base URL 和 Key 配好后就能问 SQL 相关问题。入口是 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentsqlite_crud_cursor 。第二种是长期编码和 Agent 任务。如果你在写一个需要反复调用模型的脚本或者用 Claude Code 这类工具做开发建议用 Coding Plan配置一次长期可用。地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentsqlite_crud_cursor 。配置时记住三件套Base URL 填https://taotoken.net/apiKey 填控制台创建的密钥Model ID 按实际模型填。这三样在 Claude Code、Cline MCP、Codex 的auth.json里都是必需的缺一个就连不上。如果你用的是 Codexauth.json里通常要写 Base URL、Key、Model ID 三项。Cline MCP 的配置类似在 MCP 服务配置里填这三样。Claude Code 的配置项名称可能不同但本质不变。配置完先发一条测试请求确认返回正常再进正式开发。需要新建或管理 Key 时去 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentsqlite_crud_cursor 。建议给不同项目建不同的 Key方便追踪用量也方便某个 Key 泄露时单独吊销。最后回到 sqlite 本身。增删改查和 cursor 方法是数据库入门的基本功练熟之后你会发现不管是换 MySQL 还是 PostgreSQL核心思路都一样连接、游标、执行 SQL、提交事务、关闭。区别只在驱动和占位符写法。把 sqlite 这套跑通再迁移到其他数据库成本很低。如果你在配置 TaoToken 或跑 sqlite 代码时遇到问题接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentsqlite_crud_cursor 里面有各工具的详细配置步骤。先把 Base URL、Key、Model ID 三件套核对一遍大部分连接问题都能解决。