预编译语句
创建一个将多次执行的 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 |