我正在编写一个需要使用预准备语句来获得性能优势的应用程序,但是我想知道当你有几十个(如果不是数百个)准备好的语句应用程序时,如何管理它而不会变成一团糟全球代码?你是否只需要在构造函数/函数中准备所有与使用它们完全不同的语句?
使用sqlite_exec是很好的,因为查询就在那里你实际使用它,但是使用预处理语句我最终将它们放在完全不同的代码区域中,对于需要绑定的变量,它会模糊/混淆实际使用它们的功能。
特别是现在我只有一个拥有私有变量的数据库单例,
sqlite3_stmt *insertPacketCount;
sqlite3_stmt *insertPortContacted;
sqlite3_stmt *insertPacketSize;
sqlite3_stmt *computePacketSizeVariance;
...
构造函数有几十次调用sqlite3_prepare_v2,
// Set up all our prepared queries
res = sqlite3_prepare_v2(db,
"INSERT OR IGNORE INTO packet_counts VALUES(?1, ?2, ?3, 1);",
-1, &insertPacketCount, NULL);
expectReturnValueAndFail(SQLITE_OK);
...
以及在其他地方使用它们的实际功能
void Database::IncrementPacketCount(std::string ip, std::string interface, std::string type, uint64_t increment)
{
int res;
res = sqlite3_bind_text(incrementPacketCount, 1, ip.c_str(), -1, SQLITE_STATIC);
expectReturnValue(SQLITE_OK);
res = sqlite3_bind_text(incrementPacketCount, 2, interface.c_str(), -1, SQLITE_STATIC);
expectReturnValue(SQLITE_OK);
res = sqlite3_bind_text(incrementPacketCount, 3, type.c_str(), -1, SQLITE_STATIC);
expectReturnValue(SQLITE_OK);
res = sqlite3_bind_int(incrementPacketCount, 4, increment);
expectReturnValue(SQLITE_OK);
res = sqlite3_step(incrementPacketCount);
expectReturnValue(SQLITE_DONE);
res = sqlite3_reset(incrementPacketCount);
expectReturnValue(SQLITE_OK);
}
在使用准备好的查询的函数内部,我不得不繁琐地回到prepare语句来弄清楚绑定索引是什么。
很抱歉,如果这是模糊的,但有没有任何库或方法来组织这种事情而不牺牲任何性能/安全性(比如只是追加字符串并调用sqlite_exec)?
答案 0 :(得分:2)
您不应将查询字符串从查询执行中移开。 如果你编写一个辅助函数,只在第一次需要时准备一个语句,你也可以避免在开始时准备所有语句:
void Database::PrepareStatementIfNeeded(sqlite3_stmt **stmt,
const std::string& sql)
{
if (*stmt == NULL) {
res = sqlite3_prepare_v2(db, sql.c_str(), -1, stmt, NULL);
...
}
}
(但你仍然需要确保完成所有这些。)
此外,如果您提供参数名称而不是数字,则可以使用sqlite3_bind_parameter_index来获取正确的索引:
void BindParam(sqlite3_stmt *stmt,
const char *name,
const std::string& value) // overload for other types
{
int index = sqlite3_bind_parameter_index(stmt, name);
if (index == 0)
error...;
int res = sqlite3_bind_text(stmt, index, value.c_str(), -1, SQLITE_TRANSIENT);
...
}
void Database::IncrementPacketCount(...)
{
PrepareStatementIfNeeded(&incrementPacketCount,
"INSERT OR IGNORE INTO packet_counts"
" VALUES(:ip, :intf, :type, 1)");
BindParameter(incrementPacketCount, "ip", ip);
BindParameter(incrementPacketCount, "intf", interface);
BindParameter(incrementPacketCount, "type", type);
...
}
(那些sqlite3_step
/ sqlite3_reset
调用看起来就像你重复它们一样多次;也为它们创建一个辅助函数。)