Database 手册 · Chapter 6

SELECT 模式

读取 SQL 归纳为两种流程。

模式API用途
A) 单值RunSqlScalarInt(sql)COUNT(*) 这样单格结果
B) 批量 SELECTRunSqlSelect(sql) + RowCount + GetRowArray(i) / GetValue(i, col)填充 DataGrid、多行处理

示例项目两种都用到。


模式 A — 单值查询

DB_Open() 中的空表检查是典型例子。

xscript
int rowCnt = DB["local"].RunSqlScalarInt("SELECT COUNT(*) FROM order_history");
if( rowCnt == 0 )
{
   DB_InsertInitialSamples();
}

按类型的函数:

函数返回类型
RunSqlScalarInt(sql)int
RunSqlScalarDouble(sql)double
RunSqlScalar(sql)string(通用)

查询为空时返回 0 / 0.0 / ""。失败与否用 LastError 判定。


模式 B — 批量 SELECT(DB_Refresh)

示例的 DB_Refresh() 展示标准流程。

xscript
FUNCTION DB_Refresh()
{
   if( DB["local"].IsOpen == false )
   {
      LogError($"DB_Refresh : DB is not open");
      return false;
   }
 
   string sql = "SELECT id, order_no, menu_name, start_time, end_time, weight_g, result, is_error FROM order_history ORDER BY id ASC";
   if( DB["local"].RunSqlSelect(sql) == false )
   {
      LogError($"DB_Refresh select failed : {DB["local"].LastError}");
      return false;
   }
 
   DispData.Clear();
 
   int rows = DB["local"].RowCount;
   for( i, 0, rows-1 )
   {
      // GetRowArray : 按列顺序返回一行的 XArray
      // 一条 CSV 行(用逗号拼接)= DataGrid 一行
      array row = DB["local"].GetRowArray(/*row*/i);
      string line = $"{row[0]},{row[1]},{row[2]},{row[3]},{row[4]},{row[5]},{row[6]},{row[7]}";
      DispData.Add(line);
   }
 
   Log($"DB_Refresh : {rows} rows loaded");
   return true;
}

4 步流程

  1. RunSqlSelect(sql) — 把结果缓存到内存。false 为失败。
  2. RowCount — 已缓存结果的行数。
  3. GetRowArray(i) — 第 i 行按列顺序返回为 array
  4. 结果转换 — 此处拼成一条 CSV 行加入 DispData

结果缓存在下次 SELECT 执行或连接关闭之前一直有效。

单元格访问

不按行而按单元格读取时:

xscript
DB["local"].RunSqlSelectParam("SELECT id, menu_name, weight_g FROM order_history WHERE id=?", p);
 
int    id   = DB["local"].GetValueInt(/*row*/0, /*colName*/"id");
string menu = DB["local"].GetValue(/*row*/0, /*colName*/"menu_name");
double w    = DB["local"].GetValueDouble(/*row*/0, /*colName*/"weight_g");

示例的 DB_OpenModifyDlg(第 7 章)就是用这种模式把选中行的所有列复制到编辑字段(Edit*)。

函数含义
GetValue(row, col)字符串(通用)
GetValueInt(row, col)整数
GetValueDouble(row, col)浮点
IsNull(row, col)该单元格是否为 NULL

row 从 0 开始计数,col 既可用列也可用索引(推荐用名 — 列顺序变了也安全)。

没有布尔专用访问器。示例把布尔值存为 INTEGER 0/1(is_error 列), 因此用 GetValueInt(row, col) 读取并与 1 比较。

xscript
bool isError = ( DB["local"].GetValueInt(/*row*/0, /*colName*/"is_error") == 1 );

常用 SELECT 形式

按示例领域 — 可直接复制粘贴试用:

xscript
// 1) 最近 50 条(用于 DataGrid)
"SELECT id, order_no, menu_name, end_time, result " +
"FROM order_history ORDER BY id DESC LIMIT 50"
 
// 2) 按结果分组统计
"SELECT result, COUNT(*) FROM order_history GROUP BY result"
 
// 3) 仅看错误
"SELECT id, order_no, menu_name FROM order_history WHERE is_error = 1"
 
// 4) 时间范围
"SELECT id, order_no FROM order_history " +
"WHERE start_time >= ? AND start_time <  ?"
// → 把 [from, to] 数组传给 RunSqlSelectParam

参数化 SELECT

需要参数绑定时用 RunSqlSelectParam(sql, p),而不是 RunSqlSelect(sql) — 与 INSERT/UPDATE 的 RunSqlQueryParam 相同模式。RunSqlSelect 是无参数的单参数版本, 不存在为带 ? 占位符的 SQL 接收第二参数的重载。

xscript
array p[] = {""};
p.Clear();
p.Add(targetId);
 
DB["local"].RunSqlSelectParam("SELECT * FROM order_history WHERE id=?", p);
 
if( DB["local"].RowCount == 1 )
{
   string menu = DB["local"].GetValue(0, "menu_name");
   // ...
}

常见陷阱

  • RowCount 仅在 SELECT 之后有意义 — INSERT / UPDATE 后必须再次 SELECT 才能取到准确行数。示例通过让所有变更函数最后都调用 DB_Refresh() 自动解决。
  • SELECT 中明确写出列名SELECT * 在不同环境列顺序可能不同,使用 GetRowArray 按索引访问时危险。
  • 空结果RowCount == 0 时调用 GetValue(0, ...) 返回空字符串,但逻辑上是无意义调用,务必加守卫。

下一章

读写代码都齐了,接下来是把数据放到画面上 — 只需变更 DispData 一个数组,XDataGrid 就会自动刷新。该绑定是下一章的主题。