我有一整套想要插入到表中的数据。我正在尝试让它插入/更新所有内容或回滚。我本来打算在事务中这样做,但我不确定sql_exec()命令是否做了同样的事情。
我的目标是遍历列表。根据主键从每个迭代中选择。
If result was found:
append update to string;
else
append insert to string;然后,在遍历循环之后,我将得到一个巨大的字符串,并说:
Sql_exec(字符串);sql_close(db);
我应该这么做吗?我打算在循环的每一次迭代中都这样做,但是如果有错误的话,我没有想到全局回滚。
发布于 2012-11-08 05:17:27
不,你不应该把所有的东西都附加到一条巨大的绳子里。如果您这样做了,您将需要在执行过程中分配大量内存,而且很难为每个单独的语句创建好的错误消息,因为您只会得到整个字符串的一个错误。为什么要花这么多精力来构建一个大字符串,而SQLite将不得不再次将它解析回其单独的语句中?
相反,正如@乍得所建议的那样,您应该在一个sqlite3_exec()语句上使用BEGIN语句,该语句将开始一个事务。然后依次对每个语句进行sqlite3_exec(),最后根据每件事情的进展情况,sqlite3_exec()是COMMIT还是ROLLBACK。BEGIN语句将启动一个事务,之后执行的所有语句都在该事务中,并且是这样提交或回滚到一起的。这就是酸中的"A“所代表的;原子,因为事务中的所有语句都将被提交或回滚,就好像它们是单个原子操作一样。
此外,如果每个语句中的某些数据有所不同,例如从文件中读取数据,则可能不应该使用sqlite3_exec()。如果您这样做了,一个错误可能很容易给您留下一个SQL注入错误。例如,如果您通过附加字符串来构造查询,并且有类似于char *str = "it's a string"的字符串要插入,如果您没有正确地引用它,您的语句可能会像INSERT INTO table VALUES ('it's a string');一样出现,这将是一个错误。或者,如果有人恶意地将数据写入该文件,则会导致您执行他们想要的任何SQL语句(如果字符串为"'); DROP TABLE my_important_table; --",则为"'); DROP TABLE my_important_table; --")。您可能认为没有恶意的人会提供输入,但如果有人将将SQL解析器混淆为字符串的字符放入字符串,则仍然会出现意外问题。
相反,您应该使用sqlite3_prepare_v2()和sqlite3_bind_...() (其中...是类型,比如int、double或text)。为了做到这一点,您使用一个类似于char *query = "INSERT INTO table VALUES (?)"的语句,在该语句中,您用一个?来代替您希望参数到达的位置,使用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()和COMMIT或ROLLBACK语句,或者没有来自程序外部的部分的预写查询,那么只需准备/绑定就可以了。
最后,您不需要查询数据库中是否已经有某些内容,然后插入或更新它。您可以执行INSERT OR REPLACE查询,该查询将插入记录,或者用匹配的主键替换记录,这相当于选择然后执行INSERT或UPDATE,但速度更快、更简单。有关详细信息,请参阅INSERT和“关于冲突”文档。
https://stackoverflow.com/questions/13278679
复制相似问题