Database 手册 · Chapter 5
INSERT · UPDATE · DELETE
依次了解 Data.xms 中处理变更类 SQL 的 4 个函数。
| 函数 | 作用 | 模式 |
|---|---|---|
DB_InsertSample | 新增订单 1 条 INSERT | 参数绑定 |
DB_UpdateSelected | 标记选中行为完成 | 事务 + 参数绑定 |
DB_DeleteSelected | 删除选中行 | 参数绑定 |
DB_InsertInitialSamples | 在空表中批量 INSERT 5 条 | 事务(多行) |
先整理两个核心要点。
1) 参数绑定 — RunSqlQueryParam(sql, paramArray)
不要用字符串拼接构造 SQL,把值通过数组传给 ? 占位符。可以阻止 SQL 注入,也免去引号转义之类的小麻烦。
array p[] = {""};
p.Clear();
p.Add(value1);
p.Add(value2);
// ... ? 的数量必须与 p 的元素数量完全一致
string sql = "INSERT INTO ... VALUES(?, ?, ?, ?)";
DB["local"].RunSqlQueryParam(sql, p);类型自动转换(int / double / string / bool)。
2) 事务 — BeginTransaction / Commit / Rollback
当多条 SQL 要作为整体执行,或失败时希望回滚所有变更时使用。事务中途失败时务必 Rollback — 否则下一笔事务会受影响。
if( DB["local"].BeginTransaction() == false ) return false;
if( DB["local"].RunSqlQueryParam(sql, p) == false )
{
DB["local"].Rollback();
return false;
}
if( DB["local"].Commit() == false ) return false;DB_InsertSample — 单条 INSERT
Add 按钮调用的函数。菜单在 7 项中循环,订单号通过 OrderSeq 计数器自增。
FUNCTION DB_InsertSample()
{
if( DB["local"].IsOpen == false )
{
ShowMessage(EB_Ok, "DB is not open. Press [Open] first.");
return false;
}
array menuPool[] = {"Americano", "Latte", "Cappuccino", "Espresso",
"Mocha", "Green Tea", "Lemonade"};
OrderSeq = OrderSeq + 1;
int menuIdx = OrderSeq % 7;
array p[] = {""};
p.Clear();
p.Add($"O{OrderSeq}"); // order_no
p.Add(menuPool[menuIdx]); // menu_name
p.Add(SYS.DateTimeString); // start_time
p.Add(""); // end_time(进行中,留空)
p.Add(0); // weight_g
p.Add("Pending"); // result
p.Add(0); // is_error
string sql = "INSERT INTO order_history(order_no, menu_name, start_time, end_time, weight_g, result, is_error) VALUES(?,?,?,?,?,?,?)";
if( DB["local"].RunSqlQueryParam(sql, p) == false )
{
LogError($"DB_InsertSample failed : {DB["local"].LastError}");
ShowMessage(EB_Ok, $"DB Insert failed : {DB["local"].LastError}");
return false;
}
Log($"DB_InsertSample : O{OrderSeq} {menuPool[menuIdx]} inserted");
return DB_Refresh();
}要点:
- 始终用
IsOpen把守 — 向已关闭的连接发送命令只会让 LastError 不断累积。 - 失败时
LogError+ShowMessage双输出 — 日志(追踪用)+ UI(现场提醒用)。 - 最后 调用
DB_Refresh()重新加载 DataGrid(第 6 章)。
DB_UpdateSelected — 事务化 UPDATE
Update 按钮调用。读取 DataGrid 选中行的 PK,将该行标记为 Done。
FUNCTION DB_UpdateSelected()
{
if( DB["local"].IsOpen == false )
{
ShowMessage(EB_Ok, "DB is not open. Press [Open] first.");
return false;
}
if( SelectIndex < 0 || SelectIndex >= DB["local"].RowCount )
{
ShowMessage(EB_Ok, "Select a row first.");
return false;
}
// 从 DataGrid 选中行读取 PK(id)
int targetId = DB["local"].GetValueInt(/*row*/SelectIndex, /*colName*/"id");
if( DB["local"].BeginTransaction() == false )
{
LogError($"BeginTransaction failed : {DB["local"].LastError}");
return false;
}
array p[] = {""};
p.Clear();
p.Add(SYS.DateTimeString); // end_time
p.Add(250); // weight_g(演示用固定值)
p.Add("Done"); // result
p.Add(targetId); // WHERE id = ?
string sql = "UPDATE order_history SET end_time=?, weight_g=?, result=? WHERE id=?";
if( DB["local"].RunSqlQueryParam(sql, p) == false )
{
DB["local"].Rollback();
LogError($"DB_UpdateSelected failed : {DB["local"].LastError}");
return false;
}
if( DB["local"].Commit() == false )
{
LogError($"Commit failed : {DB["local"].LastError}");
return false;
}
Log($"DB_UpdateSelected : id={targetId} updated");
return DB_Refresh();
}SelectIndex 是 DataGrid 的选中行索引(第 7 章详述)。GetValueInt(SelectIndex, "id") 这一组合是关键 — 用行号和列名读取单元格值。
DB_DeleteSelected — 简单 DELETE
FUNCTION DB_DeleteSelected()
{
// (相同的两行守卫)
int targetId = DB["local"].GetValueInt(/*row*/SelectIndex, /*colName*/"id");
array p[] = {""};
p.Clear();
p.Add(targetId);
string sql = "DELETE FROM order_history WHERE id=?";
if( DB["local"].RunSqlQueryParam(sql, p) == false )
{
LogError($"DB_DeleteSelected failed : {DB["local"].LastError}");
return false;
}
Log($"DB_DeleteSelected : id={targetId} deleted");
SelectIndex = -1;
return DB_Refresh();
}将 SelectIndex = -1 还原的原因 — 删除后再使用同一索引会指向不同的行。
DB_InsertInitialSamples — 多行事务
仅在空表首次 Open 时调用一次。将 5 条用一个事务包起来,同时获得一致性与速度。
if( DB["local"].BeginTransaction() == false ) return false;
string sql = "INSERT INTO order_history(...) VALUES(?,?,?,?,?,?,?)";
for( i, 0, 4 )
{
array p[] = {""};
p.Clear();
p.Add(/* ... */);
if( DB["local"].RunSqlQueryParam(sql, p) == false )
{
DB["local"].Rollback();
return false;
}
}
if( DB["local"].Commit() == false ) return false;5 条时差异不明显,但在真实量产环境批量装载 数百~数千 条历史数据时,事务 vs 非事务的速度差非常显著。
下一章
读取(SELECT)还没讲 — RunSqlSelect 与 GetRowArray,以及单值 RunSqlScalarInt。