不,你不应该把所有东西都附加到一个巨大的字符串中。如果你这样做了,你将需要为你分配一大堆内存,并且为每个单独的语句创建好的错误信息将会很困难,因为你只会得到整个字符串的单个错误。为什么要花费所有这些努力,在SQLite不得不再次将其解析为单个语句时构造一个大字符串?
相反,正如@Chad所建议的,您应该在BEGIN
语句中使用sqlite3_exec()
,该语句将开始一个事务。然后sqlite3_exec()
反过来每条语句,最后sqlite3_exec()
一个COMMIT
或ROLLBACK
取决于一切如何。 BEGIN
语句将启动一个事务,并且之后执行的所有语句都将在该事务中执行,并一起提交或回滚。这就是ACID代表的“A”;原子,因为事务中的所有语句将被提交或回滚,就好像它们是单个原子操作一样。
此外,你可能不应该,如果一些数据的每个语句内变化,如从文件中读取使用sqlite3_exec()
。如果你这样做,一个错误可能很容易给你带来一个SQL injection错误。例如,如果你通过追加字符串来构造查询,并且你有像char *str = "it's a string"
这样的字符串来插入,如果你没有正确引用它,你的陈述就会出现,如INSERT INTO table VALUES ('it's a string');
,这将是一个错误。或者,如果有人恶意可以将数据写入此文件,那么他们可能会导致您执行他们想要的任何SQL语句(如果字符串为"'); DROP TABLE my_important_table; --"
,则为imagine)。您可能认为没有人会提供恶意输入,但如果有人将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()
是好的调用如果查询的整个文本包含源中,如BEGIN
和COMMIT
或ROLLBACK
陈述,或没有零件从程序之外来预先写好的查询,你只需要准备/绑定,如果有任何意外的字符串可能会进入。
最后,您不需要查询数据库中是否已有某些内容,然后插入或更新它。您可以执行INSERT OR REPLACE
查询,该查询可以插入记录,也可以用匹配主键替换,这相当于选择然后执行INSERT
或UPDATE
,但更快更简单。有关更多详细信息,请参阅INSERT
和"on conflict"文档。
我会''sqlite3_exec()''BEGIN',然后单独执行每个更新。然后,您可以提交或回滚,具体取决于您是否衡量“成功”。 – Chad
@Chad为什么是评论而不是答案? –
@Fallenreaper哦,好的问题。 –