MySQL客户端/服务器协议提供了预处理语句。该功能采用了由mysql_stmt_init()初始化函数返回的MYSQL_STMT语句处理程序数据结构。对于多次执行的语句,预处理执行是一种有效的方式。首先对语句进行解析,为执行作好准备。接下来,在以后使用初始化函数返回的语句句柄执行一次或多次。
对于多次执行的语句,预处理执行比直接执行快,主要原因在于,仅对查询执行一次解析操作。在直接执行的情况下,每次执行语句时,均将进行查询。此外,由于每次执行预处理语句时仅需发送参数的数据,从而减少了网络通信量。
预处理语句的另一个优点是,它采用了二进制协议,从而使得客户端和服务器之间的数据传输更有效率。
下述语句可用作预处理语句:CREATE TABLE、DELETE、DO、INSERT、REPLACE、SELECT、SET、UPDATE、以及大多数SHOW语句。在MySQL 5.1中,不支持其他语句。
预处理语句主要使用MYSQL_STMT和MYSQL_BIND数据结构。第3种结构MYSQL_TIME用于传输暂时性数据。
· MYSQL_STMT
该结构表示预处理语句。通过调用mysql_stmt_init()创建语句,返回语句句柄,即指向MYSQL_STMT的指针。该句柄用户所有后续的与语句有关的函数,直至使用mysql_stmt_close()关闭了它为止。
MYSQL_STMT结构没有供应用程序使用的参数。此外,不应尝试复制MYSQL_STMT结构。不保证这类复制物会有用。
多个语句句柄能够与单个连接关联起来。对句柄数目的限制取决于系统资源。
· MYSQL_BIND
该结构用于语句输入(发送给服务器的数据值)和输出(从服务器返回的结果值)。对于输入,它与mysql_stmt_bind_param()一起使用,用于将参数数据值绑定到缓冲区上,以供mysql_stmt_execute()使用。对于输出,它与mysql_stmt_bind_result()一起使用,用于绑定结果缓冲区,以便用于with mysql_stmt_fetch()以获取行。
MYSQL_BIND结构包含下述供应用程序使用的成员。每个成员用于输入和输出,但在某些时候,也能用于不同的目的,具体情况取决于数据传输的方向。
o enum enum_field_types buffer_type
缓冲的类型。在本节后面列出了允许的buffer_type值。对于输入,buffer_type指明了与语句参数捆绑的值类型。对于输出,它指明了你希望从结果缓冲收到的值类型。
o void *buffer
对于输入,这是指向存储语句参数数据值的缓冲的指针。对于输出,它是指向返回结果集列值的缓冲的指针。对于数值列类型,缓冲应指向恰当的C类型变量(如果将该变量与具有UNSIGNED属性的列关联起来,变量unsigned C类型。通过使用is_unsigned成员,指明变量是signed或unsigned类型,详情请参见本节后面的介绍)。对于日期和时间列类型,缓冲应指向MYSQL_TIME结构。对于字符和二进制字符串列类型,缓冲应指向字符缓冲区。
o unsigned long buffer_length
*buffer的实际大小,单位为字节。它指明了可保存在缓冲区内的最大数据。对于字符和二进制C数据,buffer_length值指定了与mysql_stmt_bind_param()一起使用时的*buffer长度,或与mysql_stmt_bind_result()一起使用时能够提取到缓冲区内的最大数据。
o unsigned long *length
指向unsigned long变量的指针,该变量指明了存储在*buffer中数据的实际字节数。“length”用于字符或二进制C数据。对于输入参数数据绑定,“length”指向unsigned long变量,该变量指明了存储在*buffer中参数值的长度,供mysql_stmt_execute()使用。对于输出值绑定,mysql_stmt_fetch()会将返回的列值保存到“length”指向的变量中。
对于数值和临时数据类型,“length”将被忽略,原因在于,数据值的长度是由buffer_type值决定的。
o my_bool *is_null
该成员指向my_bool变量,如果值为NULL,该变量为“真”,如果值为非Null,该变量为“假”。对于输入,将*is_null设置为“真”,指明以语句参数的形式传递NULL值。对于输出,如果从语句返回的结果集列值为NULL,当获取了行后,该值将被设为“真”。
“is_null”是指向布尔类型的指针,而不是布尔标量,以便能以下述方式使用它:
§ 如果数据值总是NULL,使用MYSQL_TYPE_NULL绑定列。
§ 如果数据值总是NOT NULL,设置is_null = (my_bool*) 0。
§ 在所有其他情况下,应将is_null设置为my_bool变量的地址,并在各次执行之间恰当地更改变量的值,以指明数据值是NULL或NOT NULL。
o my_bool is_unsigned
该成员用于整数类型。(对应于MYSQL_TYPE_TINY、MYSQL_TYPE_SHORT、MYSQL_TYPE_LONG、以及MYSQL_TYPE_LONGLONG类型的代码)。对于无符号类型,应将“is_unsigned”设置为“真”,对于带符号类型,应将其设置为“假”。
o my_bool error
对于输出,该成员用于通报数据截短错误。必须通过调用带有MYSQL_REPORT_DATA_TRUNCATION选项的mysql_options(),启用截短通报功能。允许该功能后,mysql_stmt_fetch()返回MYSQL_DATA_TRUNCATED,而且对于出现截短情况的参数,在MYSQL_BIND结构中,错误标志为“真”。截短指明丢失了符号或有效位数,或字符串过长以至于无法容纳在1列中。
要想使用MYSQL_BIND结构,应将其内容置为0以便初始化它,然后对其进行设置,恰当地描述它。例如,要想声明并初始化三个MYSQL_BIND结构的数组,可使用下述代码:
MYSQL_BIND bind[3];
memset(bind, 0, sizeof(bind));
· MYSQL_TIME
该结构用于将DATE、TIME、DATETIME和TIMESTAMP数据直接发送到服务器,或从服务器直接接收这类数据。将MYSQL_BIND结构的buffer_type成员设置为临时值之一,并将buffer成员设置为指向MYSQL_TIME结构,即可实现该点。
MYSQL_TIME结构包含下述成员:
o unsigned int year
年份
o unsigned int month
月份
o unsigned int day
天
o unsigned int hour
小时
o unsigned int minute
分钟
o unsigned int second
秒
o my_bool neg
布尔标志,用于指明时间是否为负数。
o unsigned long second_part
秒的分数部分。该成员目前不使用。
仅使用施加在给定临时类型值上的MYSQL_TIME结构的部分:用于DATE、DATETIME和TIMESTAMP的年、月、日部分。用于TIME、DATETIME和TIMESTAMP值的小时、分钟、秒部分。请参见25.2.10节,“日期和时间值的C API处理”。
在下面的表格中,给出了可在MYSQL_BIND结构的buffer_type成员中指定的允许值。在该表中,还给出了与每个buffer_type值最接近的对应SQL类型,对于数值和临时类型,给出了对应的C类型。
buffer_type值 |
SQL类型 |
C类型 |
MYSQL_TYPE_TINY |
TINYINT |
char |
MYSQL_TYPE_SHORT |
SMALLINT |
short int |
MYSQL_TYPE_LONG |
INT |
int |
MYSQL_TYPE_LONGLONG |
BIGINT |
long long int |
MYSQL_TYPE_FLOAT |
FLOAT |
float |
MYSQL_TYPE_DOUBLE |
DOUBLE |
double |
MYSQL_TYPE_TIME |
TIME |
MYSQL_TIME |
MYSQL_TYPE_DATE |
DATE |
MYSQL_TIME |
MYSQL_TYPE_DATETIME |
DATETIME |
MYSQL_TIME |
MYSQL_TYPE_TIMESTAMP |
TIMESTAMP |
MYSQL_TIME |
MYSQL_TYPE_STRING |
CHAR |
|
MYSQL_TYPE_VAR_STRING |
VARCHAR |
|
MYSQL_TYPE_TINY_BLOB |
TINYBLOB/TINYTEXT |
|
MYSQL_TYPE_BLOB |
BLOB/TEXT |
|
MYSQL_TYPE_MEDIUM_BLOB |
MEDIUMBLOB/MEDIUMTEXT |
|
MYSQL_TYPE_LONG_BLOB |
LONGBLOB/LONGTEXT |
|
隐式类型转换可沿两个方向执行。
在此归纳了预处理语句处理功能可使用的函数,并在后面的章节中详细介绍了它。请参见25.2.7节,“C API预处理语句函数描述”。
函数 |
描述 |
mysql_stmt_affected_rows() |
返回由预处理语句UPDATE、DELETE或INSERT变更、删除或插入的行数目。 |
mysql_stmt_attr_get() |
获取预处理语句属性的值。 |
mysql_stmt_attr_set() |
设置预处理语句的属性。 |
mysql_stmt_bind_param() |
将应用程序数据缓冲与预处理SQL语句中的参数标记符关联起来。 |
mysql_stmt_bind_result() |
将应用程序数据缓冲与结果集中的列关联起来。 |
mysql_stmt_close() |
释放预处理语句使用的内存。 |
mysql_stmt_data_seek() |
寻找语句结果集中的任意行编号。 |
mysql_stmt_errno() |
返回上次语句执行的错误编号。 |
mysql_stmt_error() |
返回上次语句执行的错误消息。 |
mysql_stmt_execute() |
执行预处理语句。 |
mysql_stmt_fetch() |
从结果集获取数据的下一行,并返回所有绑定列的数据。 |
mysql_stmt_fetch_column() |
获取结果集当前行中某列的数据。 |
mysql_stmt_field_count() |
对于最近的语句,返回结果行的数目。 |
mysql_stmt_free_result() |
释放分配给语句句柄的资源。 |
mysql_stmt_init() |
为MYSQL_STMT结构分配内存并初始化它。 |
mysql_stmt_insert_id() |
对于预处理语句的AUTO_INCREMENT列,返回生成的ID。 |
mysql_stmt_num_rows() |
从语句缓冲结果集返回总行数。 |
mysql_stmt_param_count() |
返回预处理SQL语句中的参数数目。 |
mysql_stmt_param_metadata() |
返回结果集的参数元数据。 |
mysql_stmt_prepare() |
为执行操作准备SQL字符串。 |
mysql_stmt_reset() |
复位服务器中的语句缓冲区。 |
mysql_stmt_result_metadata() |
以结果集形式返回预处理语句元数据。 |
mysql_stmt_row_seek() |
使用从mysql_stmt_row_tell()返回的值,查找语句结果集中的行偏移。 |
mysql_stmt_row_tell() |
返回语句行光标位置。 |
mysql_stmt_send_long_data() |
将程序块中的长数据发送到服务器。 |
mysql_stmt_sqlstate() |
返回关于上次语句执行的SQLSTATE错误代码。 |
mysql_stmt_store_result() |
将完整的结果集检索到客户端。 |
调用mysql_stmt_init()以创建语句句柄,然后调用mysql_stmt_prepare准备语句,调用mysql_stmt_bind_param()提供参数数据,并调用mysql_stmt_execute()执行语句。通过更改mysql_stmt_bind_param()提供的相应缓冲区中的参数值,可重复执行mysql_stmt_execute()。
如果语句是SELECT或任何其他能生成结果集的语句,mysql_stmt_prepare()也会通过mysql_stmt_result_metadata()以MYSQL_RES结果集的形式返回结果集元数据信息。
你可以使用mysql_stmt_bind_result()提供结果缓冲,以便mysql_stmt_fetch()能自动将数据返回给这些缓冲。这是一种按行获取方式。
此外,你也能使用mysql_stmt_send_long_data()将程序块中的文本或二进制数据发送到服务器。请参见25.2.7.25节,“mysql_stmt_send_long_data()”。
完成语句执行后,必须使用mysql_stmt_close()关闭语句句柄,以便与之相关的所有资源均能被释放。
如果通过调用mysql_stmt_result_metadata()获得了SELECT语句的结果集元数据,也应使用mysql_free_result()释放元数据。
执行步骤
要想准备和执行语句,应用程序必须采取下述步骤:
1. 用msyql_stmt_init()创建预处理语句句柄。要想在服务器上准备预处理语句,可调用mysql_stmt_prepare(),并为其传递包含SQL语句的字符串。
2. 如果语句生成了结果集,调用mysql_stmt_result_metadata()以获得结果集元数据。虽然与包含查询返回列的结果集不同,该元数据本身也采用了结果集的形式。元数据结果集指明了结果中包含多少列,并包含每一列的信息。
3. 使用mysql_stmt_bind_param()设置任何参数的值。必须设置所有参数。否则,语句执行将返回错误,或生成无法预料的结果。
4. 调用mysql_stmt_execute()执行语句。
5. 如果语句生成了结果集,捆绑数据缓冲,通过调用mysql_stmt_bind_result(),检索行值。
6. 通过重复调用mysql_stmt_fetch(),按行将数据提取到缓冲区,直至未发现更多行为止。
7. 通过更改参数值并再次执行语句,重复步骤3到步骤6。
调用mysql_stmt_prepare()时,MySQL客户端/服务器协议将执行下述动作:
· 服务器解析语句,并通过赋值语句ID将OK状态发回客户端。此外,如果它是面向结果集的语句,还将发送总的参数数目,列计数和元数据。在此调用过程中,服务器将检查语句的所有语法和语义。
· 客户端采用该语句ID用于进一步操作,以便服务器能从其语句池中识别语句。
调用mysql_stmt_execute()时,MySQL客户端/服务器协议将执行下述动作:
· 客户端使用语句句柄,并将参数数据发送到服务器。
· 服务器使用由客户端提供的ID来识别语句,用新提供的数据替换参数标记符,并执行语句。如果语句生成了结果集,服务器将数据发回客户端。否则,服务器会将发送OK状态,以及总的变更、删除和插入行数。
调用mysql_stmt_fetch()时,MySQL客户端/服务器协议将执行下述动作:
· 客户端按行从信息包读取数据,并通过执行必要的转换操作将其放入应用程序数据缓冲中。如果应用程序的缓冲类型与服务器返回的字段类型相同,转换十分简明。
如果出现了错误,可分别使用mysql_stmt_errno()、mysql_stmt_error()和mysql_stmt_sqlstate()获取语句错误代码、错误消息和SQLSTATE值。
预处理语句日志功能
对于与mysql_stmt_prepare()和mysql_stmt_execute() C API函数一起执行的预处理语句,服务器会将“准备”和“执行”行写入一般查询日志,以便你能了解语句是在何时准备和执行的。
假定按下述方式准备和执行了语句:
1. 调用mysql_stmt_prepare()以准备语句字符串"SELECT ?"。
2. 调用mysql_stmt_bind_param()将值“3”绑定到预处理语句中的参数。
3. 调用mysql_stmt_execute(),执行预处理语句。
上述调用的结果是,服务器将下述行写入一般查询日志:
Prepare [1] SELECT ?
Execute [1] SELECT 3
日志中的每个“准备”和“执行”行均具有[n]语句ID标识,这样,你就能跟踪已记录的预处理语句。N是正整数。对于客户端,如果同时有多个活动的预处理语句,n可能会大于1。替换了“?”参数的数据值后,每个“执行”行将显示一条预处理语句。
版本说明:在MySQL 4.1.10之前,显示的“准备”行无[n]标识。在MySQL 4.1.10之前,不显示“执行”行