4

我有一整套数据要插入到表中。我正在尝试让它插入/更新所有内容或回滚。我打算在事务中执行此操作,但我不确定sql_exec()命令是否执行相同操作。

我的目标是遍历列表。根据主键从每次迭代中选择。

If result was found:
  append update to string;
else
  append insert to string;

然后在遍历循环之后,我会有一个巨大的字符串并说:

sql_exec(字符串); sql_close(db);

我应该这样做吗?我打算在循环的每次迭代中都这样做,但如果出现错误,我不认为全局回滚。

4

1 回答 1

6

不,您不应该将所有内容附加到一个巨大的字符串中。如果这样做,您将需要在进行时分配一大堆内存,并且为每个单独的语句创建好的错误消息将更加困难,因为您只会得到整个字符串的单个错误。当 SQLite 不得不再次将其解析回单独的语句时,为什么还要花费所有这些精力来构造一个大字符串?

相反,正如@Chad 建议的那样,您应该只使用sqlite3_exec()一个BEGIN语句,这将开始一个事务。然后sqlite3_exec()每个语句依次进行,最后sqlite3_exec()是一个COMMITROLLBACK取决于一切如何进行。该BEGIN语句将启动一个事务,之后执行的所有语句都将在该事务中,因此一起提交或回滚。这就是ACID中的“A”所代表的意思;原子,因为事务中的所有语句都将被提交或回滚,就好像它们是单个原子操作一样。

sqlite3_exec()此外,如果每个语句中的某些数据不同,例如从文件中读取,您可能不应该使用。如果你这样做了,一个错误很容易给你留下一个SQL 注入错误。例如,如果您通过附加字符串来构造查询,并且您有char *str = "it's a string"要插入的字符串,如果您没有正确引用它,您的语句可能会像INSERT INTO table VALUES ('it's a string');,这将是一个错误。或者,如果有人恶意将数据写入此文件,那么他们可能会导致您执行他们想要的任何 SQL 语句(想象一下,如果字符串是"'); DROP TABLE my_important_table; --")。您可能认为没有恶意软件会提供输入,但如果有人将一个混淆 SQL 解析器的字符放入字符串中,您仍然可能会遇到意外问题。

相反,您应该使用sqlite3_prepare_v2()and sqlite3_bind_...()...类型在哪里,例如intordoubletext)。为了做到这一点,您可以使用类似的语句char *query = "INSERT INTO table VALUES (?)",在其中将 a 替换?为您希望参数到达的位置,使用 准备它,使用sqlite3_prepare_v2(db, query, -1, &stmt, NULL)绑定参数sqlite3_bind_text(stmt, 1, str, -1, SQLITE_STATIC),然后使用 执行该语句sqlite3_step(stmt)。如果语句返回任何数据,您将获得SQLITE_ROW,并且可以使用各种sqlite3_columne_...()函数访问数据。请务必仔细阅读文档;我给出的一些示例参数可能需要根据您的使用方式进行更改。

是的,这比调用更痛苦sqlite3_exec(),但如果您的查询有任何从外部源(文件、用户输入)加载的数据,这是正确执行此操作的唯一方法。sqlite3_exec()如果查询的整个文本都包含在您的源代码中,则可以调用,例如BEGINand COMMITorROLLBACK语句,或者没有来自程序外部的部分的预先编写的查询,如果有任何机会,您只需要准备/绑定意外的字符串可能会进入。

最后,您不需要查询数据库中是否已经存在某些内容,然后插入或更新它。您可以进行INSERT OR REPLACE查询,该查询将插入一条记录,或用匹配的主键替换一条记录,这相当于选择然后执行 anINSERT或 an UPDATE,但更快更简单。有关更多详细信息,请参阅INSERT“冲突时”文档。

于 2012-11-08T05:17:27.133 回答