开发者

Delete current row in sqlite3 while iterating over a result set

Is it possible to iterate over a result set and if a condition is met, delete the current row?

i.e. something like

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 th开发者_JAVA百科e 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);

I could just call a single sql statement to delete all rows that match the criteria, but I don't want to do that if for some reason the unlink fails.

Can I call an operation to delete the current row?

EDIT: So there is a special column called rowid. Do I just add that that as a column in the previous statement and create another statement like "delete from table where rowid=?" and pass in the current rowid?

That should work right? Is this the best way of going about it?


In terms of efficiency, it's probably not the most efficient. If you're doing this for something on the level of thousands or greater number of rows, you should consider doing one (or a combination) of the following:

  • Change your query to only consider rows whose del is > 0 (SELECT id,status,filename,del FROM mytable WHERE del > 0). You're performing a table scan with your current method, which you should always try to avoid. Also make sure you have an index on the del column.

  • Build up an intermediary array of row ids, and then perform a query of the following form: DELETE FROM table WHERE id IN (?), and the parameterized value is your collected row ids joined into a comma separated string. Based on the number of rows you're dealing with, you could set this delete to be performed in batches (delete in batch sizes of 1000, 5000, etc.); since it's SQLite, tune to the device you're running with.

  • Register a custom SQLite function at connection creation time using the form:

    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);
    

and then change your database query to the form DELETE FROM mytable WHERE del > 0 AND delete_file(filename) == 0. The row will only be deleted if the delete succeeds, and you don't need to iterate over the result set. SQLite 3 create function page: http://www.sqlite.org/c3ref/create_function.html


it's OK to delete the row directly.

the doc says it doesn't interfere the running SELECT to delete a row which has already been read.

but the result is undefined if a future row that is expected to be read later is deleted.

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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜