小风儿 发表于 2018-10-2 11:29:54

mysql api---从一个小例子开始mysql编程入门(4)

  接着看
  预处理语句(Prepared Statements)
  MySQL C API还提供了另一种方式来执行SQL语句,即先预处理(prepare)再执行
  (execute). 对于多次执行的SQL语句,该方式可以提高其执行效率。具体步骤如下:


[*]调用mysql_stmt_init()创建语句句柄,该句柄在随后的函数调用中都要用到。  MYSQL_STMT mysql_stmt_init(MYSQL mysql)

  MYSQL_STMT mysql_stmt_init(MYSQL mysql)
  描述
  创建MYSQL_STMT句柄。对于该句柄,应使用mysql_stmt_close(MYSQL_STMT *)释放。
  返回值
  成功时,返回指向MYSQL_STMT结构的指针。如果内存溢出,返回NULL。
  错误
  ·         CR_OUT_OF_MEMORY
  内存溢出。


[*]  调用mysql_stmt_prepare()对SQL语句进行预处理
  int mysql_stmt_prepare(MYSQL_STMT stmt, const char stmt_str, unsigned long length)
  int mysql_stmt_prepare(MYSQL_STMT stmt, const char query, unsigned long length)
  描述
  给定mysql_stmt_init()返回的语句句柄,准备字符串查询指向的SQL语句,并返回状态值。字符串长度应由“length”参量给出。字符串必须包含1条SQL语句。不应为语句添加终结用分号(‘;’)或\g。
  通过将问号字符“?”嵌入到SQL字符串的恰当位置,应用程序可包含SQL语句中的一个或多个参数标记符。

  标记符仅在SQL语句中的特定位置时才是合法的。例如,它可以在INSERT语句的VALUES()列表中(为行指定列值),或与WHERE子句中某列的比较部分(用以指定比较值)。但是,对于ID(例如表名或列名),不允许使用它们,不允许指定二进制操作符(如等于号“=”)的操作数。后一个限制是有必要的,原因在于,无法确定参数类型。一般而言,参数仅在DML(数据操作语言)语句中才是合法的,在DDL(数据定义语言)语句中不合法。
  执行语句之前,必须使用mysql_stmt_bind_param(),将参数标记符与应用程序变量绑定在一起。
  返回值
  如果成功处理了语句,返回0。如果出现错误,返回非0值。


[*]如果SQL语句中有参数,则需要调用mysql_stmt_bind_param()进行参数绑定。  my_bool mysql_stmt_bind_param(MYSQL_STMT stmt, MYSQL_BIND bind)
  如果参数的类型为TEXT或BLOB, 并且数据量很大,可以调用
  mysql_stmt_send_long_data()来向服务器发送数据。
  my_bool mysql_stmt_send_long_data(MYSQL_STMT stmt, unsigned int parameter_number, const char data, unsigned long length)

  my_bool mysql_stmt_bind_param(MYSQL_STMT stmt, MYSQL_BIND bind)
  描述
  mysql_stmt_bind_param()用于为SQL语句中的参数标记符绑定数据,以传递给mysql_stmt_prepare()。它使用MYSQL_BIND结构来提供数据。“bind”是MYSQL_BIND结构的某一数组的地址。按照客户端库的预期,对于查询中出现的每个“?”参数标记符,数组中均包含1个元素。
  假定你准备了下述语句:
  INSERT INTO mytbl VALUES(?,?,?)
  绑定参数时,MYSQL_BIND结构的数组包含3个元素,并能声明如下:
  MYSQL_BIND bind;
  返回值
  如果绑定成功,返回0。如果出现错误,返回非0值。


[*]调用mysql_stmt_execute()来执行查询。  int mysql_stmt_execute(MYSQL_STMT *stmt)

  int mysql_stmt_execute(MYSQL_STMT *stmt)
  描述
  mysql_stmt_execute()执行与语句句柄相关的预处理查询。在该调用期间,将当前绑定的参数标记符的值发送到服务器,服务器用新提供的数据替换标记符。
  如果语句是UPDATE、DELETE或INSERT,通过调用mysql_stmt_affected_rows(),可发现更改、删除或插入的总行数。如果这是诸如SELECT等能生成结果集的语句,调用任何其他能导致查询处理的函数之前,必须调用mysql_stmt_fetch()来获取数据。
  对于生成结果集的语句,执行语句之前,可通过调用mysql_stmt_attr_set(),请求mysql_stmt_execute()为语句打开光标。如果多次执行某一语句,在打开新的光标前,mysql_stmt_execute()将关闭任何已打开的光标。
  返回值
  如果执行成功,返回0。如果出现错误,返回非0值。


[*]若查询不产生结果集,可以调用  mysql_stmt_affected_rows()和
  my_ulonglong mysql_stmt_insert_id(MYSQL_STMT stmt)来获得被改变的记录数和生成的自增值。
  my_ulonglong mysql_stmt_affected_rows(MYSQL_STMT stmt)
  my_ulonglong mysql_stmt_insert_id(MYSQL_STMT stmt)
  否则,执行mysql_stmt_bind_result()对结果集中的字段进行绑定。
  my_bool mysql_stmt_bind_result(MYSQL_STMT stmt, MYSQL_BIND *bind)

  my_ulonglong mysql_stmt_affected_rows(MYSQL_STMT *stmt)
  描述
  返回上次执行语句更改、删除或插入的总行数。对于UPDATE、DELETE或INSERT语句,可在mysql_stmt_execute()之后立刻调用它们。对于SELECT语句,mysql_stmt_affected_rows()的工作方式类似于mysql_num_rows()。
  返回值
  大于0的整数指明了受影响或检索的行数。对于UPDATE语句,“0”表明未更新任何记录,在查询中没有与WHERE子句匹配的行,或尚未执行任何查询。“-1”表明返回了错误,或对SELECT查询,在调用mysql_stmt_store_result()之前调用了mysql_stmt_affected_rows()。由于mysql_stmt_affected_rows()返回无符号值,可通过比较返回值和“(my_ulonglong)-1”(或等效的“(my_ulonglong)~0”),检查“-1”。


[*]调用mysql_stmt_fetch()来逐行获取结果集中的记录。  int mysql_stmt_fetch(MYSQL_STMT stmt)
  在调用mysql_stmt_fetch()之前,还可以执行mysql_stmt_store_result()将结果
  集预先缓存到本地。
  int mysql_stmt_store_result(MYSQL_STMT stmt)

  int mysql_stmt_store_result(MYSQL_STMT *stmt)
  描述
  对于成功生成结果集的所有语句(SELECT、SHOW、DESCRIBE、EXPLAIN),而且仅当你打算对客户端的全部结果集进行缓冲处理时,必须调用mysql_stmt_store_result(),以便后续的mysql_stmt_fetch()调用能返回缓冲数据。
  对于其他语句,没有必要调用mysql_stmt_store_result(),但如果调用了它,也不会造成任何伤害或导致任何性能问题。通过检查mysql_stmt_result_metadata()是否返回NULL,可检测语句是否生成了结果集。
  返回值
  如果成功完成了对结果的缓冲处理,返回0。如果出现错误,返回非0值。


[*]重复步骤3-6, 每次使用不同的实参来执行查询。
[*]调用mysql_stmt_close()关闭句柄,释放资源  my_bool mysql_stmt_close(MYSQL_STMT )
  此外,可以调用mysql_stmt_errno()和mysql_stmt_error()来获知最近执行的预处
  理语句API函数的错误代码和错误信息。
  unsigned int mysql_stmt_errno(MYSQL_STMT stmt)
  const char mysql_stmt_error(MYSQL_STMT stmt)

  总体而言:
  MySQL客户端/服务器协议提供了预处理语句。该功能采用了由mysql_stmt_init()初始化函数返回的MYSQL_STMT语句处理程序数据结构。对于多次执行的语句,预处理执行是一种有效的方式。首先对语句进行解析,为执行作好准备。接下来,在以后使用初始化函数返回的语句句柄执行一次或多次。
  对于多次执行的语句,预处理执行比直接执行快,主要原因在于,仅对查询执行一次解析操作。在直接执行的情况下,每次执行语句时,均将进行查询。此外,由于每次执行预处理语句时仅需发送参数的数据,从而减少了网络通信量。
  预处理语句的另一个优点是,它采用了二进制协议,从而使得客户端和服务器之间的数据传输更有效率。
  编程步骤
  1.用mysql_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。
  实现代码:
  #include
  #include
  #include
  #include
  #define STRING_SIZE 50
  #define DROP_SAMPLE_TABLE "DROP TABLE IF EXISTS test_table"
  #define CREATE_SAMPLE_TABLE "CREATE TABLE test_table(col1 INT,\
  col2 VARCHAR(40),\
  col3 SMALLINT,\
  col4 TIMESTAMP)"
  #define INSERT_SAMPLE "INSERT INTO test_table(col1,col2,col3) VALUES(?,?,?)"
  //int main(char *argv, int argc)
  int main(char argv[], int argc)
  {
  int   ret = 0, i = 0;
  

MYSQL   *mysql;  
MYSQL   *connect = NULL;
  
char    sqlbuf;
  

  
mysql = mysql_init(NULL) ;
  
if (mysql == NULL)
  
{
  ret =mysql_errno(&mysql) ;
  printf("func mysql_init() err \n");
  return ret;
  
}
  
printf("func mysql_init() ok \n");
  

  
connect = mysql_real_connect(mysql, "localhost", "root", "123456", "mydb2", 0, NULL, 0);
  
if (connect == NULL)
  
{
  ret =mysql_errno(&mysql) ;
  printf("func mysql_init() err \n");
  return ret;
  
}
  
printf("func mysql_real_connect() ok \n");
  

  
MYSQL_STMT    *stmt;
  
MYSQL_BIND    bind;
  
my_ulonglongaffected_rows;
  
int         param_count;
  
short         small_data;
  
int         int_data;
  
char          str_data;
  
unsigned long str_length;
  
my_bool       is_null;
  

  
if (mysql_query(mysql, DROP_SAMPLE_TABLE))
  
{
  fprintf(stderr, " DROP TABLE failed\n");
  fprintf(stderr, " %s\n", mysql_error(mysql));
  exit(0);
  
}
  

  
if (mysql_query(mysql, CREATE_SAMPLE_TABLE))
  
{
  fprintf(stderr, " CREATE TABLE failed\n");
  fprintf(stderr, " %s\n", mysql_error(mysql));
  exit(0);
  
}
  

  
/* Prepare an INSERT query with 3 parameters */
  
/* (the TIMESTAMP column is not named; the server */
  
/*sets it to the current date and time) */
  
stmt = mysql_stmt_init(mysql);//初始化预处理环境句柄MYSQL_STMT *stmt
  
if (!stmt)
  
{
  fprintf(stderr, " mysql_stmt_init(), out of memory\n");
  exit(0);
  
}
  
if (mysql_stmt_prepare(stmt, INSERT_SAMPLE, strlen(INSERT_SAMPLE)))//向预处理环境句柄stmt 中添加sql( 带有占位符)
  
{
  fprintf(stderr, " mysql_stmt_prepare(), INSERT failed\n");
  fprintf(stderr, " %s\n", mysql_stmt_error(stmt));
  exit(0);
  
}
  
fprintf(stdout, " prepare, INSERT successful\n");
  

  
/* Get the parameter count from the statement */
  
param_count= mysql_stmt_param_count(stmt);   //获取sql语言中 占位符 的个数
  
fprintf(stdout, " total parameters in INSERT: %d\n", param_count);
  

  
if (param_count != 3) /* validate parameter count */
  
{
  fprintf(stderr, " invalid parameter count returned by MySQL\n");
  exit(0);
  
}
  

  
/* Bind the data for all 3 parameters */
  


  
memset(bind, 0,>  

  
/* INTEGER PARAM */
  
/* This is a number type, so there is no need to specify buffer_length */
  
bind.buffer_type= MYSQL_TYPE_LONG;//设置第一个占位符的属性
  
bind.buffer= (char *)&int_data;
  
bind.is_null= 0;
  
bind.length= 0;
  

  
/* STRING PARAM */
  
bind.buffer_type= MYSQL_TYPE_STRING; //设置第2个占位符的属性
  
bind.buffer= (char *)str_data;
  
bind.buffer_length= STRING_SIZE;
  
bind.is_null= 0;
  
bind.length= &str_length;
  

  
/* SMALLINT PARAM */
  
bind.buffer_type= MYSQL_TYPE_SHORT; //设置第3个占位符的属性
  
bind.buffer= (char *)&small_data;
  
bind.is_null= &is_null;
  
bind.length= 0;
  

  
/* Bind the buffers */
  
if (mysql_stmt_bind_param(stmt, bind))//把设置好的属性 加入预处理环境stmt中
  
{
  fprintf(stderr, " mysql_stmt_bind_param() failed\n");
  fprintf(stderr, " %s\n", mysql_stmt_error(stmt));
  exit(0);
  
}
  

  
/* Specify the data values for the first row */    //准备插入数据
  
int_data= 10;             /* integer */
  
strncpy(str_data, "MySQL", STRING_SIZE); /* string*/
  
str_length= strlen(str_data);
  

  
/* INSERT SMALLINT data as NULL */
  
is_null= 1;
  

  
/* Execute the INSERT statement - 1*/
  
if (mysql_stmt_execute(stmt))   //执行预处理环境插入第一条记录
  
{
  fprintf(stderr, " mysql_stmt_execute(), 1 failed\n");
  fprintf(stderr, " %s\n", mysql_stmt_error(stmt));
  exit(0);
  
}
  

  
/* Get the total number of affected rows */
  
affected_rows= mysql_stmt_affected_rows(stmt);//获取受影响的行信息
  
fprintf(stdout, " total affected rows(insert 1): %lu\n",
  (unsigned long) affected_rows);
  

  
if (affected_rows != 1) /* validate affected rows */
  
{
  fprintf(stderr, " invalid affected rows by MySQL\n");
  exit(0);
  
}
  

  
/* Specify data values for second row, then re-execute the statement */
  
int_data= 1000;
  
strncpy(str_data, "The most popular Open Source database", STRING_SIZE);
  
str_length= strlen(str_data);
  
small_data= 1000;         /* smallint */
  
is_null= 0;               /* reset */
  

  
/* Execute the INSERT statement - 2*/
  
if (mysql_stmt_execute(stmt))    //插入第二条记录
  
{
  fprintf(stderr, " mysql_stmt_execute, 2 failed\n");
  fprintf(stderr, " %s\n", mysql_stmt_error(stmt));
  exit(0);
  
}
  

  
/* Get the total rows affected */
  
affected_rows= mysql_stmt_affected_rows(stmt); //有获取受影响的行
  
fprintf(stdout, " total affected rows(insert 2): %lu\n",
  (unsigned long) affected_rows);
  

  
if (affected_rows != 1) /* validate affected rows */
  
{
  fprintf(stderr, " invalid affected rows by MySQL\n");
  exit(0);
  
}
  

  
/* Close the statement */
  
if (mysql_stmt_close(stmt))
  
{
  fprintf(stderr, " failed while closing the statement\n");
  fprintf(stderr, " %s\n", mysql_stmt_error(stmt));
  exit(0);
  
}
  

  mysql_close(connect);
  

  
printf("hello...\n");
  
return ret;
  

  }
  下一节将说明:多语句与时间相关的 处理方式 请看
  mysql api---从一个小例子开始mysql编程入门(5)


页: [1]
查看完整版本: mysql api---从一个小例子开始mysql编程入门(4)