预编译语句
创建一个将多次执行的 SQL 语句是有助于性能提升的。在 McoSqlEngine 类中,这是通过调用 prepare() 或 vprepare() 方法之一来完成的,这些方法将 SQL 语句编译为 PreparedStatement 实例。
这样做的优点是每次执行该语句时都省去了语句编译步骤,并且只需一次绑定语句参数指针。
但由于仅使用指针参数,应用程序必须在整个预编译语句的生命周期内保持传递给 prepare() 的参数变量处于作用域内,并且在执行该预编译语句时确保参数具有实际值。
有关参数替换格式规范的详细列表,请参阅参数替换格式说明。
由于 Python、Java 和 C# 语言中不存在指针。因此不支持预编译语句。
prepare()
prepare() 方法定义如下:
virtual void prepare(PreparedStatement &stmt, char const* sql, ...);调用 executePreparedStatement() 方法来执行预编译的语句,或者调用 executePreparedQuery() 方法来执行查询并返回结果集。 例如,以下代码片段定义并编译了 5 个 预编译的语句:
#include <mcosql.h>
#include <commonSQL.h>
#include "sqldb.h"
McoSqlEngine engine;
void Prepare()
{
int2 delta;
unt1 aid;
unt4 tid;
int8 bid;
char historyFiller[10] = “----------“;
PreparedStatement stmt[5];
engine.open( db_name, sqldb_get_dictionary(), DATABASE_SIZE, MEMORY_PAGE_SIZE);
...
engine.prepare(stmt[0], "UPDATE accounts SET Abalance=Abalance+%*i2 WHERE Aid=%*u1",
&delta, &aid);
engine.prepare(stmt[1], "SELECT Abalance FROM accounts WHERE Aid=%*u1", &aid);
engine.prepare(stmt[2], "UPDATE tellers SET Tbalance=Tbalance+%*i2 WHERE Tid=%*u4",
&delta, &tid);
engine.prepare(stmt[3], "UPDATE branches SET Bbalance=Bbalance+%*i2 WHERE Bid=%*i8",
&delta, &bid);
engine.prepare(stmt[4],
"INSERT INTO account_history(Tid, Bid, Aid, delta, htime, filler) VALUES”
“ (%*u4,%*i8,%*u1,%*i2,now,%s)", &tid, &bid, &aid, &delta, historyFiller);
...
}请注意,对于有符号和无符号整数变量,分别使用“%*iN”和“%*uN”占位符,其中“N”表示整数宽度。
然后可以按如下方式执行这些语句:
McoSqlEngine engine;
void ExecuteStatements()
{
engine.executePreparedStatement(stmt[0]);
engine.executePreparedStatement(stmt[2]);
engine.executePreparedStatement(stmt[3]);
engine.executePreparedStatement(stmt[4]);
}查询是通过为查询结果集实例化一个 QueryResult 来执行的,如下所示:
McoSqlEngine engine;
void PerformQuery()
{
QueryResult *result = engine.executePreparedQuery(stmt[1]);
Cursor * cursor = result->records();
while (cursor->hasNext())
{
Record * rec = cursor->next();
// 处理结果行
...
}
}请注意,也可以在一个线程中准备一条语句,而在另一个线程中执行该语句。在执行预编译查询时,PreparedStatement 类的对象必须一直存在,直到释放生成的 DataSource 对象为止。否则,结果集的内部结构可能会遭到破坏。
vprepare()
prepare() 方法使用可变参数列表。McoSqlEngine 的 SqlEngine 基类提供了两个额外的“底层” vprepare() 方法:
virtual void vprepare(PreparedStatement &stmt, char const* sql, va_list* list);
virtual void vprepare(PreparedStatement &stmt, char const* sql, ParamDesc* params);这两种方法中的第一种使用了 va_list 参数,实际上在 prepare() 的实现中是这样使用的:
void SqlEngine::prepare(PreparedStatement &stmt, char const* sql, ...)
{
va_list list;
va_start(list, sql);
vprepare(stmt, sql, &list);
va_end(list);
}第二种方法需要创建一个 ParamDesc 实例的数组,这些实例指定了每个参数的类型和地址。例如:
#include <mcosql.h>
#include <commonSQL.h>
#include "sqldb.h"
McoSqlEngine engine;
int main( int argc, char ** argv )
{
PreparedStatement stmt;
ParamDesc params[2];
int4 id = 0;
char str[10];
params[0].type = tpInt4;
params[0].ptr = &id;
params[0].lenptr = NULL;
params[1].type = tpString;
params[1].ptr = &str;
params[1].lenptr = NULL;
...
engine.open( db_name, sqldb_get_dictionary(), DATABASE_SIZE, MEMORY_PAGE_SIZE);
engine.vprepare(stmt, "insert into AnObject values (?,?)", params);
id = 1; strcpy(str, "one");
engine.executePreparedStatement(stmt);
id = 2; strcpy(str, "two");
engine.executePreparedStatement(stmt);
id = 3; strcpy(str, "three");
engine.executePreparedStatement(stmt);
...
} ParamDesc 结构在 include/sql/sqlcpp.h 中定义如下:
struct ParamDesc
{
Type type;
void* ptr;
int* lenptr;
bool indirectStr;
};-
type:变量类型 - ``ptr` :变量地址。
lenptr一个整数的地址。- 对于类型为
tpBinary的字段,必须指定lenptr,该整数为变量的长度; - 对于数据类型
tpString和tpUnicode,lenptr是可选的,该整数为变量的长度。如果将lenptr设置为NULL,则将根据由字段 ptr 指定的字符串的实际大小自动计算其大小。 - 对于所有其他数据类型,应将其设置为
NULL。 indirectStr:- 对于类型为
char*或wchar_t*的变量,必须将indirectStr元素设置为false; - 对于类型为
char**或wchar_t**的变量,则必须将其设置为true; - 可选地,在
lenptr元素中指定字符串变量的大小。
- 对于类型为
请注意,通常作为参数传递的字符串是指向字符串首字符的指针。例如以下字符串定义:
const char *mystring = "The Stranglers";可以作为参数传递给带有替换占位符 %S 的 executeStatement() 方法。这可以称为“直接字符串指针”。相比之下,预处理语句通过间接(通过指针)访问参数。因此,替换占位符 %s 表示指向字符串的指针,而该字符串本身又是指向字符的指针。例如,可以使用以下定义来表示“间接字符串”参数:
const char *mystring = "Ramones";
const char **ptr_to_string = &mystring; 以下代码片段演示了如何将字符串参数与 prepare() 以及 vprepare() 方法的 ParmDesc 变体一起使用:
const char *mystring = "The Stranglers";
const char **ptr_to_string = &mystring;
PreparedStatement stmt;
// 间接字符串‘prepare’
engine.prepare(stmt, "select * from Bands where name=%*s", ptr_to_string);
// 间接字符串'vprepare'
ParamDesc desc;
desc.type = tpString;
desc.ptr = ptr_to_string;
desc.lenptr = NULL;
desc. indirectStr = true;
engine.vprepare("select * from Bands where name=?", &desc);
// 直接字符串'prepare'
engine.prepare(stmt, "select * from Bands where name=%*S", mystring);
// 直接字符串'vprepare'
desc.type = tpString;
desc.ptr = mystring;
desc.lenptr = NULL;
desc. indirectStr = false;
engine.vprepare("select * from Bands where name=?", &desc);对于宽字符字符串也是如此。替换占位符 %w 假定其参数类型为 wchar_t *(“指向宽字符指针的指针”),而占位符 %W 则假定其参数类型为 wchar_t。
ParmDesc类型
在 include/sql/value.h 中定义了 type 元素的有效值以及对应的 C/C++ 变量类型:
| type元素值 | C/C++ 变量类型 |
|---|---|
| tpNull | NULL |
| tpBool, tpInt1, tpUInt1 | char |
| tpInt2 | short |
| tpUInt2 | unsigned short |
| tpInt4 | int |
| tpUInt4 | unsigned int |
| tpInt8 | long int |
| tpUInt8 | uint64_t |
| tpReal4 | float |
| tpReal8 | double |
| tpDateTime | mco_datetime (unsigned int) |
| tpNumeric | double |
| tpUnicode | wstring (wchar_t*) |
| tpString | string (char *) |
| tpBinary | byte[] |
| tpInt = tpInt8 | int64_t |
| tpReal = tpReal8 | double |
| tpReference | autoid_t |
