不,您不应该将所有内容附加到一个巨大的字符串中。如果这样做,您将需要在进行时分配一大堆内存,并且为每个单独的语句创建好的错误消息将更加困难,因为您只会得到整个字符串的单个错误。当 SQLite 不得不再次将其解析回单独的语句时,为什么还要花费所有这些精力来构造一个大字符串?
相反,正如@Chad 建议的那样,您应该只使用sqlite3_exec()
一个BEGIN
语句,这将开始一个事务。然后sqlite3_exec()
每个语句依次进行,最后sqlite3_exec()
是一个COMMIT
或ROLLBACK
取决于一切如何进行。该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_...()
(...
类型在哪里,例如int
ordouble
或text
)。为了做到这一点,您可以使用类似的语句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()
如果查询的整个文本都包含在您的源代码中,则可以调用,例如BEGIN
and COMMIT
orROLLBACK
语句,或者没有来自程序外部的部分的预先编写的查询,如果有任何机会,您只需要准备/绑定意外的字符串可能会进入。
最后,您不需要查询数据库中是否已经存在某些内容,然后插入或更新它。您可以进行INSERT OR REPLACE
查询,该查询将插入一条记录,或用匹配的主键替换一条记录,这相当于选择然后执行 anINSERT
或 an UPDATE
,但更快更简单。有关更多详细信息,请参阅INSERT
和“冲突时”文档。