![FireDAC 下的 Sqlite [5]:插入、更新、删除的配置骨架与验证](http://pic.xiahunao.cn/yaotu/FireDAC 下的 Sqlite [5]:插入、更新、删除的配置骨架与验证)
1. FireDAC 连上 Sqlite 之后增删改到底该怎么写才不翻车FireDAC 下的 Sqlite 插入、更新、删除是 Delphi 开发者把本地库真正用起来的第一道坎。连接能通、查询能出数据不代表写入就稳参数占位符写错、事务没提交、Refresh 时机不对、自增主键回填拿不到任何一个都能让你盯着 DBGrid 发呆。这篇就聚焦 FireDAC 连接 Sqlite 之后的增删改落地配置给你一套可复制的 FDConnection / FDQuery 参数骨架和 SQL 模板插入、更新、删除各演示一步验证动作保证数据变更可回滚、可复现。适合谁看已经能在 Delphi 里用 FireDAC 打开 Sqlite、会拖控件、但一写 INSERT/UPDATE/DELETE 就心里没底的同学。全文用最朴素的 VCL 窗体加按钮来跑不依赖第三方框架你照着敲就能复现。核心检索词就三个FireDAC、Sqlite、插入更新删除下面所有配置都围绕它们展开。我试过把参数化 SQL 和字符串拼接混着用结果遇到带单引号的姓名直接报语法错后来统一改成占位符才消停。所以本文的骨架一律走参数绑定不给你留拼接的坑。2. 前置准备TaoToken 与 FireDAC 环境各就各位2.1 FireDAC 侧要拖的控件在空白窗体上放这几个TFDConnection、TFDPhysSQLiteDriverLink、TFDGUIxWaitCursor、TFDQuery、TDataSource、TDBGrid再加四个 TButton 分别对应插入、更新、删除、查询。设计期把 FDQuery1.Connection 指向 FDConnection1DataSource1.DataSet 指向 FDQuery1DBGrid1.DataSource 指向 DataSource1。FDGUIxWaitCursor 的 Provider 设成 Forms避免控制台程序里等待光标报错。2.2 如果你还要接大模型做辅助生成写 SQL 骨架时想让模型帮你补参数、解释报错可以先把 TaoToken 的 Key 配好。注册入口在官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 登录后在控制台创建 API Keyhttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。接口地址统一用 https://taotoken.net/api 不要带 UTM 后缀。想先验证模型通不通用模型对话页 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_campaignrewrite 。接入细节查文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite Claude Code 相关看 https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_codeutm_campaignrewrite 。这部分只是辅助主战场还是 FireDAC 本身。3. 可复制配置FDConnection 与 FDQuery 参数骨架3.1 建库建表与连接串先看建库这一段。数据库文件放在固定路径方便反复删除重建保证每次实验都是干净状态。procedure TForm1.FormCreate(Sender: TObject); const dbPath C:\Temp\SQLiteTest.sdb; strTable CREATE TABLE MyTable( Id integer PRIMARY KEY AUTOINCREMENT, Name string(10), Age byte); begin if FileExists(dbPath) then DeleteFile(dbPath); FDConnection1.ConnectionString : DriverIDSQLite; Database dbPath; FDConnection1.ExecSQL(strTable); FDQuery1.Open(SELECT * FROM MyTable); end;这里DriverIDSQLite是 FireDAC 识别 Sqlite 驱动的关键Database指向具体文件。integer PRIMARY KEY AUTOINCREMENT让 Id 自增插入时不用管它。建表用 ExecSQL查询用 Open两者别混。3.2 参数化插入模板插入统一走占位符值用数组按顺序给比 Format 拼接安全得多。procedure TForm1.Button1Click(Sender: TObject); const strInsert INSERT INTO MyTable(Name, Age) VALUES(:name, :age); begin FDConnection1.ExecSQL(strInsert, [AAA, 11]); FDConnection1.ExecSQL(strInsert, [BBB, 22]); FDConnection1.ExecSQL(strInsert, [CCC, 33]); FDConnection1.ExecSQL(strInsert, [DDD, 44]); FDConnection1.ExecSQL(strInsert, [EEE, 55]); FDQuery1.Refresh; end;:name、:age是命名占位符数组顺序必须和 SQL 里出现的顺序一致。写错顺序不会报错但数据会串位这是最常见的隐形坑。3.3 更新与删除模板更新用 WHERE 锁定目标行删除用条件批量清。procedure TForm1.Button2Click(Sender: TObject); begin FDConnection1.ExecSQL(UPDATE MyTable SET Age:a WHERE Name:n, [Random(100), AAA]); FDQuery1.Refresh; end; procedure TForm1.Button3Click(Sender: TObject); begin FDConnection1.ExecSQL(DELETE FROM MyTable WHERE Age33); FDQuery1.Refresh; end;更新里:a在前、:n在后数组就是[Random(100), AAA]顺序对齐。删除条件Age33会一次干掉多行执行前最好先 SELECT 数一下影响范围。3.4 参数对照表配置项取值说明DriverIDSQLite指定 Sqlite 驱动DatabaseC:\Temp\SQLiteTest.sdb库文件绝对路径占位符:name / :age命名参数数组按序赋值刷新方式FDQuery1.Refresh写操作后同步 DBGrid自增字段Id AUTOINCREMENT插入时不赋值注意ExecSQL 用于 INSERT/UPDATE/DELETEOpen 用于 SELECT用反了会抛异常或拿不到结果集。4. 验证请求插入、更新、删除各跑一步看结果4.1 插入验证点 Button1 后DBGrid 应出现五行AAA/11、BBB/22、CCC/33、DDD/44、EEE/55。如果只出现部分检查数组顺序和 Refresh 是否执行。想确认自增主键可以加一句查询var V: Variant; begin V : FDConnection1.ExecSQLScalar(SELECT Age FROM MyTable WHERE Name :x, [BBB]); ShowMessage(VarToStr(V)); end;ExecSQLScalar 拿单值这里应弹出 22。它适合做存在性校验比如插入后确认某行确实落库。4.2 更新验证点 Button2AAA 的 Age 会被随机数覆盖。因为用了 Random(100)每次点结果不同正好验证更新确实生效。DBGrid 里 AAA 那行的 Age 变了说明 UPDATE 命中。若没变先确认 WHERE 的 Name 值拼写和库里一致Sqlite 默认区分大小写。4.3 删除验证点 Button3Age 大于 33 的行被清掉。按上面的数据DDD/44、EEE/55 会消失剩下 AAA、BBB、CCC。删除后 DBGrid 行数减少就是成功信号。想更稳可以在删除前用 ExecSQLScalar 数一下SELECT COUNT(*) FROM MyTable WHERE Age33心里有数再动手。4.4 事务与回滚默认每条 ExecSQL 自动提交。要保证一组操作可回滚显式开事务FDConnection1.StartTransaction; try FDConnection1.ExecSQL(INSERT INTO MyTable(Name, Age) VALUES(:n, :a), [FFF, 66]); FDConnection1.ExecSQL(UPDATE MyTable SET Age:a WHERE Name:n, [77, FFF]); FDConnection1.Commit; except FDConnection1.Rollback; raise; end;Commit 前任何一步出错Rollback 都能把整组撤销这就是可回滚、可复现的基础。5. 本篇常见错排查5.1 报 no such table: MyTable多半是建表没执行成功或者 Database 路径指向了另一个文件。检查 FormCreate 里 ExecSQL(strTable) 是否真的跑了路径是否和查询时一致。Sqlite 不会自动建库文件路径写错会新建一个空库表自然不存在。5.2 参数数量不匹配报 Array index out of bounds 或参数个数错误通常是占位符数量和数组元素数量对不上。数一遍 SQL 里的冒号再数数组一一对应。命名占位符重复出现时FireDAC 按出现顺序取值重复的也要重复给值。5.3 更新删除影响 0 行SQL 没报错但数据没变先查 WHERE 条件。Name 值带空格、大小写不一致、Age 类型不匹配都会导致命中 0 行。用 ExecSQLScalar 先 SELECT 同条件数一下能快速定位。5.4 DBGrid 不刷新写操作后忘了 FDQuery1.Refresh界面还是旧数据。Refresh 会重新执行当前 SELECT代价不大增删改后都加上。如果 Refresh 报数据集未打开先确认 FDQuery1 处于 Active 状态。5.5 自增 Id 拿不到插入后想立刻拿新 Id用SELECT last_insert_rowid()var NewId: Int64; begin FDConnection1.ExecSQL(INSERT INTO MyTable(Name, Age) VALUES(:n, :a), [GGG, 88]); NewId : FDConnection1.ExecSQLScalar(SELECT last_insert_rowid()); ShowMessage(IntToStr(NewId)); end;这是 Sqlite 的标准做法别去猜 Id 值。6. 把骨架用起来下一步怎么接到这里FireDAC 下 Sqlite 的插入、更新、删除骨架就齐了连接串固定 DriverID 加 Database写操作走 ExecSQL 加命名占位符读操作走 Open改完 Refresh成组操作套事务。你可以把这套模板直接搬进自己的项目把表名和字段换掉即可。如果你在写这些 SQL 时想让模型帮忙补参数、解释报错或者做代码审查可以走 TaoToken 的 APIhttps://taotoken.net/api Key 在 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 创建接入方式看文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。长期做 Delphi 编码和 Agent 自动化的Coding Plan 在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。先把上面四个按钮跑通再谈扩展。