【问题标题】:Delete current row in sqlite3 while iterating over a result set迭代结果集时删除 sqlite3 中的当前行
【发布时间】:2021-04-25 17:44:59
【问题描述】:

是否可以遍历结果集,如果满足条件,则删除当前行?

即像

int rc;
sqlite3_stmt* statement;
sqlite3_exec(db, "BEGIN", 0, 0, 0);

sqlite3_prepare_v2(_db, "SELECT id,status,filename,del FROM mytable", -1, &statement, NULL);

rc = sqlite3_step(statement);
while (rc == SQLITE_ROW){
  int id = sqlite3_column_int(statement, 1);
  int status = sqlite3_column_int(statement, 2);
  const unsigned char* filename = sqlite3_column_int(statement, 3);
  int del = sqlite3_column_int(statement, 4);

  if (status == 0 || del > 0){
    int rc = unlink(filename);
    if (rc == 0)
      // Now delete the current row
    else 
      // unlink failed, find out why, try again or ... ?
  } 

  rc = sqlite3_step(statement);
}
sqlite3_finalize(statement);
sqlite3_exec(db, "COMMIT", 0, 0, 0);

我可以只调用一个 sql 语句来删除所有符合条件的行,但如果由于某种原因取消链接失败,我不想这样做。

我可以调用一个操作来删除当前行吗?

编辑: 所以有一个特殊的列叫rowid。我是否只是将其添加为 上一条语句并创建另一个语句,例如“从 rowid = 的表中删除?”并传入当前的rowid?

应该可以吧?这是最好的方法吗?

【问题讨论】:

  • 好的,我用rowid让它工作了。但仍然想知道这是否是最有效的方法。

标签: c sqlite


【解决方案1】:

就效率而言,它可能不是最有效的。如果您要为数千行或更多行数的内容执行此操作,则应考虑执行以下操作之一(或组合):

  • 将查询更改为仅考虑 del > 0 (SELECT id,status,filename,del FROM mytable WHERE del > 0) 的行。您正在使用当前方法执行表扫描,您应该始终尽量避免。还要确保你在 del 列上有一个索引。

  • 建立一个中间行ID数组,然后执行以下形式的查询:DELETE FROM table WHERE id IN (?),参数化值是您收集的行ID连接成一个逗号分隔的字符串。根据您正在处理的行数,您可以将此删除设置为批量执行(批量删除 1000、5000 等);因为它是 SQLite,所以调整到你正在运行的设备。

  • 在连接创建时使用以下形式注册自定义 SQLite 函数:

    void deleteFileFunc(sqlite3_context * context, int argc, sqlite3_value ** argv) {
      assert(argc == 1);
      const char * fileName = sqlite3_value_text(argv[0]);
      int rc = unlink(fileName);
      sqlite3_result_int(context, rc);
    }
    sqlite3_create_function(db, "delete_file", 1, SQLITE3_UTF8, NULL, &deleteFileFunc, NULL, NULL);
    

然后将您的数据库查询更改为DELETE FROM mytable WHERE del > 0 AND delete_file(filename) == 0 的形式。仅当删除成功时才会删除该行,并且您不需要遍历结果集。 SQLite 3 创建函数页面:http://www.sqlite.org/c3ref/create_function.html

【讨论】:

  • 谢谢,这是非常有用的信息。我没有意识到 sqlite 支持存储过程之类的东西。
  • 没有问题 - 应该指出这个特殊的东西是一个 UDF(用户定义的函数)而不是真正的存储过程。每次重新打开该数据库时,您都需要使用 sqlite3_create_function 创建函数。
【解决方案2】:

直接删除行即可。

文档说它不会干扰正在运行的 SELECT 删除已读取的行。

但如果预期稍后读取的未来行被删除,则结果未定义。

https://www.sqlite.org/isolation.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-17
    • 1970-01-01
    • 2013-05-09
    • 2016-07-11
    相关资源
    最近更新 更多