2011-05-15 wcdj
(一) 不返回数据的SQL语句 —— UPDATE、DELETE 和 INSERT
(二) 发现插入的内容
(三) 返回数据的语句
(1) 一次提取所有数据的函数
(2) 一次提取一行数据的函数
(三) 返回数据的语句
SQL最常见的用法当然是提取数据而不是插入或更新数据。数据是使用SELECT语句提取的。
说明 :MySQL也支持使用SQL语句SHOW、DESCRIBE和EXPLAIN来返回结果,但我们不会在这里涉及它们。按照惯例,手册中包含了对这些语句的解释。
在C应用程序中提取数据一般需要下面4个步骤 :
【1】执行查询
【2】提取数据
【3】处理数据
【4】必要的清理工作
使用mysql_query来发送SQL语句,然后使用mysql_store_result或mysql_use_result来提取数据,接着,使用一些列mysql_fetch_row调用来处理数据。最后,使用mysql_free_result释放查询占用的内存资源。
说明 :mysql_use_result和mysql_store_result的区别 :主要在于,你是想一次返回一行数据,还是一次返回所有的结果。当你预计结果集比较小时,后者会更加合适。
(1) 一次提取所有数据的函数
可以使用mysql_store_result在一次调用中从SELECT(或其他返回数据的语句)中提取所有数据:
MYSQL_RES *mysql_store_result( MYSQL *connection );
你需要在成功调用mysql_query之后使用此函数。这个函数将立刻保存在客户端中返回的所有数据。它返回一个指向结果集结构的指针,如果失败则返回NULL。
在mysql_store_result调用成功之后,你需要调用mysql_num_rows来得到返回记录的数目,我们希望这是个正数,但是如果没有返回行,这个值将是0。
my_ulonglong mysql_num_rows( MYSQL_RES *result );
这个函数接受由mysql_store_result返回的结果结构,并返回结果集中的行数。如果mysql_store_result调用成功,mysql_num_rows将始终都是成功的。
注意 :
如果碰巧使用的是一个特别 的数据集,那么最后提取小一些、更容易管理的信息块,因为这将更快地将控制权返回给应用程序,并且不会占用大量的网络资源。具体参考mysql_use_result的用法。
现在,你可以使用mysql_fetch_row来处理它,也可以使用mysql_data_seek、mysql_row_seek和mysql_row_tell在数据集中来回移动。
[1] mysql_fetch_row
这个函数从使用mysql_store_result得到的结果结构中提取一行,并把它放到一个行结构中。当数据用完或发生错误时返回NULL。
MYSQL_ROW mysql_fetch_row( MYSQL_RES *result );
[2] mysql_data_seek
这个函数用来在结果集中进行跳转,设置将会被下一个mysql_fetch_row操作返回的行。参数offset的值是一个行号,它必须在0到结果集总行数减1的范围内。传递0将会导致下一个mysql_fetch_row调用返回结果集中的第一行。
void mysql_data_seek( MYSQL_RES *result, my_ulonglong offset );
[3] mysql_row_tell
这个函数返回一个偏移值,它用来表示结果集中的当前位置。它不是行号,你不能把它用于mysql_data_seek。
MYSQL_ROW_OFFSET mysql_row_tell( MYSQL_RES *result );
[4] mysql_row_seek
可以在mysql_row_seek中使用mysql_row_tell的返回值。
MYSQL_ROW_OFFSET mysql_row_seek( MYSQL_RES *result, MYSQL_ROW_OFFSET offset );
这将在结果集中移动当前位置,并返回之前的位置。
注意:
这对函数对于在结果集中的已知点之间的移动非常有用。但请小心不要混淆了由row_tell和row_seek使用的偏移量和data_seek使用的行号。否则,结果将变得不可预知。
[5] mysql_free_result
void mysql_free_result( MYSQL_RES *result );
完成了对数据集的操作后,你必须总是调用此函数来让MySQL库清理它分配的对象。
例子:
选择所有年龄大于100的记录。
select1.c
使用用户wcdj登录mysql
$ mysql -u wcdj -p newdatabase
Enter password:
Welcome to the MySQL monitor. Commands end with ; or /g.
显示表中的所有数据
mysql> SELECT * FROM children;
#include
#include
#include "mysql.h"
MYSQL my_connection;
MYSQL_RES *res_ptr;
MYSQL_ROW sqlrow;
int main(int argc, char *argv[])
{
int res;
mysql_init(&my_connection);
if(mysql_real_connect(&my_connection, "localhost", "wcdj", "123", "newdatabase", 0, NULL, 0))
{
printf("Connection success/n");
res = mysql_query(&my_connection, "SELECT childno, fname, age FROM children WHERE age > 100");
if (res)
{
printf("SELECT error: %s/n", mysql_error(&my_connection));
}
else// ok
{
res_ptr = mysql_store_result(&my_connection);
if (res_ptr)// ok
{
printf("Retrieved %lu rows/n", (unsigned long)mysql_num_rows(res_ptr));// ok
while ((sqlrow = mysql_fetch_row(res_ptr)))
{
printf("Fetched data.../n");// ok
}
if (mysql_errno(&my_connection))
{
fprintf(stderr, "Retrive error: %s/n", mysql_error(&my_connection));
}
mysql_free_result(res_ptr);
}
}
mysql_close(&my_connection);
}
else
{
fprintf(stderr, "Connection failed/n");
if (mysql_errno(&my_connection))
{
fprintf(stderr, "Connection error %d: %s/n", mysql_errno(&my_connection),
mysql_error(&my_connection));
}
}
return EXIT_SUCCESS;
}
编译程序:
$ gcc -I/usr/include/mysql select1.c -L/usr/lib/mysql -lmysqlclient -o select1
(2) 一次提取一行数据的函数
MYSQL_RES *mysql_use_result(MYSQL *connection);
与mysql_store_result函数一样,mysql_use_result在遇到错误时也返回NULL。如果成功,它返回指向结果集对象的指针。但是,不同之处在于它未将提取的数据放到它初始化的结果集中。
注意 :
为了真正得到数据,你必须反复调用mysql_fetch_row直到提取了所有的数据。如果没有从mysql_use_result中得到所有数据,那么程序中后续的提取数据操作可能会返回遭到破坏的信息。
调用mysql_use_result和调用mysql_store_result的效果有何不同 呢?
【1】mysql_use_result的缺点
1、前者具备资源管理方面的实质性好处,但是它不能与mysql_data_seek、mysql_row_seek或mysql_row_tell一起使用,并且由于直到所有数据都被提取后才能实际生效,mysql_num_rows的使用也受到限制。
2、而且,还增加了时延,因为每个行请求和结果的返回都必须通过网络。
3、另外还存在一种可能性是,网络连接可能在操作中途失败,留给你不完整的数据。
【2】mysql_use_result的优点
但是,无论怎样,上述缺点都不会抹去mysql_use_result带来的好处:更好地平衡了网络负载,以及减少了可能非常大的数据集带来的存储开销。
例子:
这里使用mysql_use_result函数
select2.c
#include
#include
#include "mysql.h"
MYSQL my_connection;
MYSQL_RES *res_ptr;
MYSQL_ROW sqlrow;
int main(int argc, char *argv[])
{
int res;
mysql_init(&my_connection);
if(mysql_real_connect(&my_connection, "localhost", "wcdj", "123", "newdatabase", 0, NULL, 0))
{
printf("Connection success/n");
res = mysql_query(&my_connection, "SELECT childno, fname, age FROM children WHERE age > 100");
if (res)
{
printf("SELECT error: %s/n", mysql_error(&my_connection));
}
else// ok
{
//res_ptr = mysql_store_result(&my_connection);
res_ptr = mysql_use_result(&my_connection);// mysql_use_result
if (res_ptr)// ok
{
printf("Retrieved %lu rows/n", (unsigned long)mysql_num_rows(res_ptr));// ok, 0
while ((sqlrow = mysql_fetch_row(res_ptr)))
{
printf("Fetched data.../n");// ok
}
if (mysql_errno(&my_connection))
{
fprintf(stderr, "Retrive error: %s/n", mysql_error(&my_connection));
}
mysql_free_result(res_ptr);
}
}
mysql_close(&my_connection);
}
else
{
fprintf(stderr, "Connection failed/n");
if (mysql_errno(&my_connection))
{
fprintf(stderr, "Connection error %d: %s/n", mysql_errno(&my_connection),
mysql_error(&my_connection));
}
}
return EXIT_SUCCESS;
}
编译程序:
$ gcc -I/usr/include/mysql select2.c -L/usr/lib/mysql -lmysqlclient -o select2
注意 :
在提取最后一个结果之前,你仍然无法得到行数(测试,mysql_nuw_rows此时返回结果为0)。但是,通过早期和经常性的错误检查,可以使得程序调整为使用mysql_use_result变得更加容易。以这种方式编写代码可以减少许多程序后期修改带来的烦恼。
参考 :
SQL语言基本语句介绍
http://www.longen.org/s-z/details~z/SqlSentence.htm Linux 程序设计(第4版)第8章 P.293