不,你不應該把所有東西都附加到一個巨大的字符串中。如果你這樣做了,你將需要爲你分配一大堆內存,並且爲每個單獨的語句創建好的錯誤信息將會很困難,因爲你只會得到整個字符串的單個錯誤。爲什麼要花費所有這些努力,在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哦,好的問題。 –