Linux(程序设计):18---C语言访问MySQL(mysql.h)
时间:2019-10-22
本文章向大家介绍Linux(程序设计):18---C语言访问MySQL(mysql.h),主要包括Linux(程序设计):18---C语言访问MySQL(mysql.h)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
一、C语言访问MySQL的前提
①安装了MySQL服务端
②安装MySQL库
- 使用C语言访问MySQL之前需要安装MySQL库,输入以下命令安装:
sudo apt-get install libmysqlclient-dev
③gcc编译MySQL程序
gcc -I/usr/include/mysql demo.c -L/usr/lib/mysql -lmysqlclient -o demo
- 使用-I添加include路径
- 使用-L添加库文件路径
- -lmysqlclient指定链接的库模块
- 备注:在某些系统上,你可能还需要使用-lz选项来链接压缩库
④mysql头文件
- 系统中的mysql头文件在/usr/include/mysql/目录下
二、连接句柄初始化(mysql_init)
-
-
-
MYSQL *mysql_init(MYSQL *mysql);
- 功能:用来初始化连接句柄
- 参数:
- 如果为NULL,则通过返回值返回一个指向新分配的连接句柄结构的指针
- 如果传递一个 已有的结构,它将被重新初始化
- 返回值:出错返回NULL
三、MySQL的连接、断开(mysql_real_connect、mysql_close)
MySQL的连接
MYSQL * mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag);
- 功能:用来连接数据库
- 参数:
- mysql:必须指向已经被mysql_init初始化过的结构
- host:既可以是主机名,也可以是IP地址(如果只是连接到本地机器,你可以通过 指定localhost来优化连接类型)
- user:登录用户(如果登录名为NULL,则假设登录 名为当前Linux用户的登录ID)
- passwd:用户的密码(如果密码为NULL,你将只能访问服务器上无需密码就可访问的数据),密码会在通过网络传输前进行加密
- db:要访问的数据库
- port:应该填为0,他们会自动默认使用合适的值(除非你改变了MySQL安装的默认设置)
- unix_socket:应该填为NULL,他们会自动默认使用合适的值(除非你改变了MySQL安装的默认设置)
- clientflag:用来对一些定义的位模式进行OR操作,使得改变使用协议的某些特性。一般填0
- 返回值:
- 如果出错,返回NULL,mysql_error函数可以提供有帮助的信息
MySQL的断开
void mysql_close(MYSQL *sock);
- 功能:关闭连接
- 如果连接是由mysql_init建立的,MySQL结构会被释放。指针将会失效并无法再次使用
- 保留一个不需要的连接是对资源的浪费,但是重新打开连接也会带来额外的开销,你可以使用下面的mysql_options选项函数来权衡选择适合的选项
四、MySQL选项设置(mysql_options)
-
-
-
int mysql_options(MYSQL *mysql,enum mysql_option option,const void *arg);
- 功能:用来设置MySQL连接的一些选项
- 注意:
- ①仅能在mysql_init和mysql_real_connect之间调用
- ②因为mysql_options一次只能设置一个选项,所以每设置一个选项就得调用它一次
- 参数:
- mysql:MySQL连接句柄
- option:设置的选项
- arg:相对于option选项要设置的值,在使用时需要强制转换为(const char*)
- option参数如下:
option选项 | 实际参数类型 | 说明 |
MySQL_OPT_CONNECT_TIMEOUT | const unsigned int * | 连接超时之前的等待秒数 |
MySQL_OPT_COMPRESS | None,使用NULL | 网络连接中使用压缩机制 |
MySQL_INIT_COMMAND | const char * | 每次连接建立后发送的命令 |
mysql.h中定义的选项选项: |
- 返回值:
- 成功:返回0
- 失败:返回非0
五、MySQL错误处理(mysql_errno、mysql_error)
- 因为每次调用库都会更新错误码,所以你只能得到最后一个执行命令的错误码。 但是这两个函数是例外,它们不会导致错误码的更新
返回错误码(mysql_errno)
unsigned int mysql_errno(MYSQL *mysql);
- 功能:传递MySQL句柄,通过返回值返回错误码,错误码通常是非0值
- 返回值:
- 如果有错误:错误码通过返回值返回,错误码通常是非0值
- 如果没有错误:返回值为0
- 错误码定义头文件文件:
- /usr/include/mysql/mysqld_error.h:主要定义服务端的错误编码
- /usr/include/mysql/errmsg.h:主要定义客户端错误
返回错误字符串 (mysql_error)
const char * mysql_error(MYSQL *mysql);
- 功能:传递MySQL句柄,返回错误的文本形式
演示案例
//connect2.c int main(int argc,char *argv[]) { MYSQL mysql; mysql_init(&mysql); if(mysql_real_connect(&mysql,"localhost","root", "I do not know","demo",0,NULL,0)){ printf("Connect success\n"); mysql_close(&mysql); }else{ fprintf(stderr,"Connect failed:\n"); if(mysql_errno(&mysql)){ printf("\terror code is %d\n\treason:%s\n",mysql_errno(&mysql),mysql_error(&mysql)); } } return 0; }
- 编译程序:
gcc -I/usr/include/mysql connect2.c -L/usr/lib/mysql -lmysqlclient -o connect2
- 运行程序:因为上面我们连接数据库的用户密码错误了,所以打印1045错误编码 ,并且打印了错误字符串
六、执行SQL语句(mysql_query、mysql_real_query)
- 在编写C语言程序时,SQL语句尽量不要换行。如果SQL语句过长,不得不换行,可以在行尾使用\字符以允许SQL 语句继续到下一行
mysql_query
int mysql_query(MYSQL *mysql, const char *q);
- 功能:用来执行一条SQL语句(增、删、改、查)
- 参数:
- mysql:MySQL句柄
- q:执行的SQL语句文本字符串,不需要分号
- 返回值:
- 执行成功:返回0
- 执行失败:返回非0
mysql_real_query
int mysql_real_query(MYSQL *mysql, const char *q,unsigned long length);
- 功能:该函数用来查询二进制数据
- 与mysql_query的区别:mysql_query不能用于二进制数据,该函数可以用于二进制数据查询
- 返回值:
- 查询成功返回0
- 查询成功返回非0
演示案例
- 因为涉及到博主的数据库的密码,下面我们的mysql_real_connect函数的数据库密码使用“xxx”表示(出于隐私)
//insert1.c int main(int argc,char *argv[]) { MYSQL mysql; mysql_init(&mysql); if(mysql_real_connect(&mysql,"localhost","root", "xxx","demo",0,NULL,0)){ printf("Connect success\n"); //insert if(mysql_query(&mysql,"insert into children(fname,age) values('Ann',18)")!=0){ fprintf(stderr,"mysql_query failed:\n\tcode:%d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); }else{ printf("Insert success,affect row are %lu\n", mysql_affected_rows(&mysql)); } mysql_close(&mysql); }else{ fprintf(stderr,"Connect failed:\n"); if(mysql_errno(&mysql)){ printf("\terror code is %d\n\treason:%s\n",mysql_errno(&mysql),mysql_error(&mysql)); } } return 0; }
- 进入数据库,在demo数据中创建一张表(编号字段自动增加)
- 编译程序
gcc -I/usr/include/mysql insert1.c -L/usr/lib/mysql -lmysqlclient -o insert1
- 运行程序
- 查看数据库的信息
七、检查受影响的行数(mysql_affected_rows)
-
-
-
my_ulonglong mysql_affected_rows(MYSQL *mysql);
- 功能:用于检查受查询影响的行数,这个函数返回受之前执行的UPDATE、INSERT或DELETE语句影响的行数
- 返回值:
- 返回0:没有行受影响
- 正数:受影响的行数
- 返回值类型my_ulonglong:无符号类型,在prinf打印时,强制转换为unsigned long类型,然后使用%lu占位符打印
- where条件导致的影响行数:在执行一条SQL语句时,如果SQL语句带有where条件,那么虽然有与where匹配的行,当时如果原先的数据与要更改的数据相同,那么这一行就不会被认为更改过了。但是其他数据库语言仅仅因为记录匹 配WHERE子句就把它视为已经更新过(详情见下面的演示案例①)
mysql_real_connect函数的CLIENT_FOUND_ROWS标志
- 在执行mysql_affected_rows函数时,MySQL会默认省略掉与where匹配但是数据一样的行,从而不会再mysql_affected_rows函数的返回值中记录这一行数。如果在使用mysql_real_connect函数时,将函数的最后一个参数置位CLIENT_FOUND_ROWS,那么即使匹配的那一行数据不用更新也会被记录(详情见下面的演示案例①)
删除数据时的一种特殊情况
- 在从数据库中删除数据的时候。如果使用WHERE子句删除数据,那么mysql_affected_rows将返回你期望的删除的行数。但如果在DELETE语句中没有WHERE子句(重点:一定要是没有where的删除语句),那么表中的所有行都会被删除,但是由程序返回的受影响行数却为0。 这是因为MySQL优化了删除所有行的操作,它并不是执行许多个单行删除操作。这一行为不会受CLIENT_FOUND_ROWS选项标志的影响
- 这种情况是我在看书时书上说的,但是我在Ubuntu中做了实验,这样特殊情况并不存在,所以到底是怎么一回事,还是与实际操作有关
演示案例①
- 因为涉及到博主的数据库的密码,下面我们的mysql_real_connect函数的数据库密码使用“xxx”表示(出于隐私)
//update1.c int main(int argc,char *argv[]) { MYSQL mysql; mysql_init(&mysql); if(mysql_real_connect(&mysql,"localhost","root", "xxx","demo",0,NULL,0)){ printf("Connect success\n"); //insert if(mysql_query(&mysql,"update children set age=20 where fname='Ann'")!=0){ fprintf(stderr,"mysql_query failed:\n\tcode:%d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); }else{ printf("Update success,affect row are %lu\n", mysql_affected_rows(&mysql)); } mysql_close(&mysql); }else{ fprintf(stderr,"Connect failed:\n"); if(mysql_errno(&mysql)){ printf("\terror code is %d\n\treason:%s\n",mysql_errno(&mysql),mysql_error(&mysql)); } } return 0; }
- 运行程序前,查看数据库,名为Ann的行数,有2行age为18,有2行age为20
- 编译程序:
gcc -I/usr/include/mysql update1.c -L/usr/lib/mysql -lmysqlclient -o update1
- 运行程序:
- 可以看到数据库中名为Ann的行有4行,但是更新之后受影响的行数只有2行,因为另外两行虽然与SQL语句匹配,但是由于其qge已经为20了,就没有更新,因此也就没有被认为被影响
- 我们复制上面的updae1.c程序,并新建一个update2.c程序,将mysql_real_connect函数的最后一个参数设置为CLIENT_FOUND_ROWS
- 更改数据库,更改age字段为原先的两行18,两行20
- 编译程序:
gcc -I/usr/include/mysql update2.c -L/usr/lib/mysql -lmysqlclient -o update2
- 运行程序:
- 可以看到mysql_real_connect函数加上了CLIENT_FOUND_ROWS标志之后,即使有的行数没有更改数据,但是那2行被where条件匹配了,也就算在了影响的行数中
八、MySQL的last_insert_id函数
- 注意:last_inser_id是MySQL数据库中的一个函数,而不是C语言提供的API
last_inser_id函数应用的场景
- 插入数据有一个微小但至关重要的方面。MySQL表中的数据类型为AUTO_INCREMENT的字段,它由MySQL自动分配ID。这一特性非常有用,特别是当你有许多用户的时候
- 例如在上面我们定义的children时,将childno字段类型设置为AUTO_INCREMENT
- 正如你看到的那样,childno列被设为AUTO_INCREMENT类型。这样当然很好,但是一旦你插入一 行,你如何知道刚插入的孩子被分配了什么数字呢?
- 你可以执行一条SELECT语句来搜索孩子的名字,但这样效率会很低,并且如果有两个相同名字的 孩子,这将不能保证唯一性。或者,如果同时有多个用户快速地插入数据,那么可能在更新操作和 SELECT语句之间会有其他行被插入。因为发现一个AUTO_INCREMENT列的值是大家都面临的一个共同 问题,所以MySQL以函数LAST_INSERT_ID()的形式提供了一个专门的解决方案
- 无论何时MySQL向AUTO_INCREMENT列中插入数据,MySQL都会基于每个用户对最后分配的值进 行跟踪。用户程序可以通过SELECT专用函数LAST_INSERT_ID()来发现该值,这个函数的作用有点像 是表中的虚拟列
MySQL操作演示案例
- 先查看表中没有任何数据
- 此时插入一条语句,并查看last_insert_id函数的返回值
- 此时再插入一条语句,并查看last_insert_id函数的返回值
- 查看数据库信息,可以看到插入的childno的值与我们的last_insert_id函数返回的值一样
- 实验解析:每次插入一行,MySQL就分配一个新的id值并且跟踪它,使得你可以用LAST_INSERT_ID()来提取它
如果想通过实验查看返回的数字在本次会话中确实是唯一的,那么你可以打开另一个会话并插入 另一行数据。然后在最初的会话中重新执行SELECT LAST_INSERT_ID();语句。你将看到数字并没有 发生改变,这是因为该语句返回的数字是由当前会话插入的最后一个数字。但是,如果执行SELECT * FROM children,你将看到其他会话确实已插入数据了
- 此时,我们再新建一个新的会话窗口(与先前的不是同一个会话),并且使用LAST_INSERT_ID()返回插入的ID值,可以看到为15
- 此时,我们来到第一个会话窗口执行LAST_INSERT_ID()函数,发现其值还是14。然后查询表数据,可以看到另一个会话插入的数据已经插入了(由此可以得出LAST_INSERT_ID()函数返回的数字在本次会话中是唯一的)
C语言演示案例
- 因为涉及到博主的数据库的密码,下面我们的mysql_real_connect函数的数据库密码使用“xxx”表示(出于隐私)
//insert2.c int main(int argc,char *argv[]) { MYSQL mysql; MYSQL_RES *res_ptr; MYSQL_ROW sqlrow; mysql_init(&mysql); if(mysql_real_connect(&mysql,"localhost","root", "xxx","demo",0,NULL,0)){ printf("Connect success\n"); //insert if(mysql_query(&mysql,"insert into children(fname,age) values('Robert',7)")!=0){ fprintf(stderr,"mysql_query failed:\n\tcode:%d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); }else{ printf("Insert %lu rows\n",(unsigned long)mysql_affected_rows(&mysql)); } //select last_insert_id() if(mysql_query(&mysql,"select last_insert_id()")!=0){ fprintf(stderr,"mysql_query failed:\n\tcode:%d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); }else{ res_ptr=mysql_use_result(&mysql) if(res_ptr){ while((sqlrow=mysql_fetch_row(res_ptr))){ printf("We inserted childno %s\n",sqlrow[0]); } mysql_free_result(res_ptr); } } mysql_close(&mysql); }else{ fprintf(stderr,"Connect failed:\n"); if(mysql_errno(&mysql)){ printf("\terror code is %d\n\treason:%s\n",mysql_errno(&mysql),mysql_error(&mysql)); } } return 0; }
- 编译程序:
gcc -I/usr/include/mysql insert2.c -L/usr/lib/mysql -lmysqlclient -o insert2
- 运行程序:在插入一行之后,你用LAST_INSERT_ID()函数来获取分配的ID,就像常规的SELECT语句一样。 然后使用mysql_use_result()从执行的SELECT语句中获取数据并将它打印出来,我们稍后将解释此函数
九、MySQL查询:一次提取所有数据(mysql_store_result)
在C应用程序中提取数据一般需要下面4个步骤:
- 执行查询(mysql_query)
- 提取数据(mysql_store_result或mysql_use_result)
- 处理数据(mysql_fetch_row)
- 必要的清理工作(mysql_free_result)
MYSQL_RES、MYSQL_ROW、MYSQL_ROW_OFFSET数据类型
- MYSQL_RES:代表从SELECT(或其他返回数据的语句)中提取所有数据,是一个结果集结构
- MYSQL_ROW:代表MYSQL_RES数据集中的一行,是一个行结构
- 下标操作,因为MYSQL_ROW代表一行数据,我们可以使用索引的方式取出一行中的对应下标数据,例如MYSQL_ROW row,row[0]、row[1]、row[2]
- MYSQL_ROW_OFFSET:代表当前程序在MYSQL_RES结果集中所操作的行索引
mysql_store_result:一次提取所有数据
MYSQL_RES *mysql_store_result(MYSQL *mysql);
- 功能:mysql_store_result在一次调用中从SELECT(或其他返回数据的语句)中提取所有数据
- 参数:MySQL的连接句柄
- 返回值:
- 成功:保存查询到的结果集结构指针
- 失败:NULL
- 返回值的注意事项:提示没有提取到数据,这个函数还是返回一个MYSQL_RES指针,而不是NULL,NULL只有在函数出错时返回。因此在编程时判断该函数出错的条件应该是该函数返回NULL
- mysql_use_result是一次只从结果中提取一行数据,置于两者的区别,我们在下面介绍mysql_use_result函数时会讲解
mysql_num_rows:获取返回的行数
my_ulonglong mysql_num_rows(MYSQL_RES *result);
- 功能:函数参数为mysql_store_result返回的结果集结构(MYSQL_RES),返回结果集中的行数
- 返回值:
- 正数:为返回的行数。在prinf打印时,强制转换为unsigned long类型,然后使用%lu占位符打印
- 0:没有返回行数
- 如果mysql_store_ result调用成功,mysql_num_rows将始终都是成功的
mysql_fetch_row:提取一行
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
- 功能:从mysql_store_result得到的结果集结构(MYSQL_RES)中提取一行,并把它放到一个行结构中
- 返回值:
- 成功:返回一个行结构(MYSQL_ROW)
- 数据用完或失败:返回NULL
- 这个函数每次只能从MYSQL_RES中提取一行,因此想要提取所有行,需要不断的调用该函数
mysql_data_seek、mysql_row_tell、mysql_row_seek:行数索引操作
void mysql_data_seek(MYSQL_RES *result,my_ulonglong offset); MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES *res); MYSQL_ROW_OFFSET mysql_row_seek(MYSQL_RES *result,MYSQL_ROW_OFFSET offset);
- mysql_data_seek:这个函数用来在结果集中进行跳转,设置了offset之后,下一次mysql_fetch_row读取的行就是这个函数所设置的行
- 参数result:结果集
- 参数offset:行号,必须在0到结果集总行数减1的范围内。传递0会导致下一个mysql_fetch_row调用返回结果集中的第一行
- mysql_row_tell:这个函数返回一个偏移值,它用来表示结果集中的当前位置。但返回值不是行号,而是一种MYSQL_ROW_OFFSET数据类型
- mysql_row_seek:该函数用于将当前的位置移动到参数offset所指的位置(不是行号,是MYSQL_ROW_OFFSET类型),函数的返回值是该函数执行之前的位置(重点)
备注:
- 不要把mysql_data_seek函数和mysql_row_tell、mysql_row_seek函数所操作的行数混淆,前者是整型,后两者是MYSQL_ROW_OFFSET数据类型
mysql_free_result:清除操作/善后处理
void mysql_free_result(MYSQL_RES *result);
- 功能:完成了对结果集的操作后,你必须总是调用此函数来让MySQL库清理它分配的对象
- 完成了对数据的所有操作后,你必须明确地调用mysql_free_result来让MySQL库完成善后处理
演示案例
- 因为涉及到博主的数据库的密码,下面我们的mysql_real_connect函数的数据库密码使用“xxx”表示(出于隐私)
- 下面的程序只是简单的获取返回的行数有多少行,而没有对返回的数据进行操作,对数据进行操作将在下面的演示案例中讲解
//select1.c int main(int argc,char *argv[]) { MYSQL mysql; MYSQL_RES *res; MYSQL_ROW sqlrow; mysql_init(&mysql); if(mysql_real_connect(&mysql,"localhost","root", "xxx","demo",0,NULL,0)){ printf("Connect success\n"); //select if(mysql_query(&mysql,"select childno,fname,age from children where age>5")!=0){ fprintf(stderr,"mysql_query failed:\n\tcode:%d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); }else{ //get result res=mysql_store_result(&mysql); if(res!=NULL){ //show rows printf("Retrived %lu rows:\n",(unsigned long)mysql_num_rows(res)); //Get one row at a time while(sqlrow=mysql_fetch_row(res)){ printf("\tFetch row...\n"); } }else{ if(mysql_errno(&mysql)){ printf("\terror code is %d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); } } } mysql_close(&mysql); }else{ fprintf(stderr,"Connect failed:\n"); if(mysql_errno(&mysql)){ printf("\terror code is %d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); } } return 0; }
- 查看数据库信息(可以看到age>5的有3条记录):
- 编译程序:
gcc -I/usr/include/mysql select1.c -L/usr/lib/mysql -lmysqlclient -o select1
- 运行程序:
十、MySQL查询:一次提取一行数据(mysql_use_result)
-
-
-
MYSQL_RES *mysql_use_result(MYSQL *mysql);
- 功能:逐行提取数据
- 返回值:
- 正确:与mysql_store_result一样,保存查询到的结果集结构指针
- 失败:返回NULL
工作原理
- 这个函数在执行之后,其返回值MYSQL_RES是空的,也就是说其未将提取的数据放到它初始化的结果集中
- 接着,会在mysql_use_result函数之后调用mysql_fetch_row函数,并将mysql_use_result函数的返回值作为mysql_fetch_row函数的参数,然后使用while循环执行mysql_fetch_row函数,mysql_fetch_row函数会每执行一次才从网络中获取一行数据
- 因此,我们所说的一次提取一行数据并不是发生在mysql_use_result函数提取一行,而是在后面调用mysql_fetch_row函数时才正真的一次只提取一行(重点)
- 所以,想要获得多少行数据,就调用多少次mysql_fetch_row函数,其调用次数与获取的行数是1:1的关系。想要获取所有数据,就是while循环不断执行mysql_fetch_row函数
mysql_num_rows函数的注意事项
- 在没有调用mysql_fetch_row之前,mysql_num_rows总是返回0,因此此时程序还没有正确的获取数据
- 每当调用一次mysql_fetch_row,mysql_num_rows函数所获取的行数就会加1,因此调用mysql_fetch_row之后,此时程序中就被认为获取了一行数据
与mysql_store_result到底有什么区别:
- 对于mysql_store_resul来说,其一次性直接获取所有的查询数据,并且查询的数据时保存在本地的,使用这个函数可以不用担心网络或远程数据库错误带来数据丢失的影响
- 对于mysql_use_result来说,其备资源管理方面的实质性好处(更好地平衡了网络负载,以及 减少了可能非常大的数据集带来的存储开销),但是它不能与mysql_data_seek、mysql_row_seek或mysql_row_tell一起使用,并且由于直到所有数据都被提取后才能实际生效,mysql_num_rows的使用也受到限制(见下面演示案例)
- 如果你碰巧使用的是一个特别庞大的数据集,那么最好使用mysql_use_result函数提取小一些、更容易管理的信息块,因为这将更快地将控制权返回给应用程序,并且不会占用大量的网络资源
演示案例
- 因为涉及到博主的数据库的密码,下面我们的mysql_real_connect函数的数据库密码使用“xxx”表示(出于隐私)
//select2.c int main(int argc,char *argv[]) { MYSQL mysql; MYSQL_RES *res; MYSQL_ROW sqlrow; mysql_init(&mysql); if(mysql_real_connect(&mysql,"localhost","root", "xxx","demo",0,NULL,0)){ printf("Connect success\n"); //select if(mysql_query(&mysql,"select childno,fname,age from children where age>5")!=0){ fprintf(stderr,"mysql_query failed:\n\tcode:%d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); }else{ //get result res=mysql_use_result(&mysql); if(res!=NULL){ //Get one row at a time while(sqlrow=mysql_fetch_row(res)){ printf("\tFetch row...\n"); } }else{ if(mysql_errno(&mysql)){ printf("\terror code is %d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); } } } mysql_close(&mysql); }else{ fprintf(stderr,"Connect failed:\n"); if(mysql_errno(&mysql)){ printf("\terror code is %d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); } } return 0; }
- 查看数据库:
- 编译程序:
gcc -I/usr/include/mysql select2.c -L/usr/lib/mysql -lmysqlclient -o select2
- 运行结果:因为mysql_num_rows放置到了mysql_fetch_row函数的前面,此时程序还没有正真的从数据库中获取数据,因此mysql_num_rows函数显示为0
- 现在我们更改程序:复制select2.c,新建select3.c程序,将程序中的mysql_num_rows函数放置到mysql_fetch_row循环中
- 运行select3.c结果:可以看到每次调用mysql_fetch_row获取一行数据之后,mysql_num_rows函数再去执行就可以获取到行数了
十一、得到返回的列(字段)数(mysql_field_count)
- 在上面我们介绍了如何提取行,下面学习如何处理返回的数据了。如同大多数SQL数据库一样,MySQL返回两种类型的数据:
- 列数据:从表中提取的信息
- 元数据:关于数据的数据,例如列名和类型
-
-
-
unsigned int mysql_field_count(MYSQL *mysql);
- 功能:提供了一些关于查询结果的基本信息,它接受连接对象,并返回结果集中的字段(列)数目
- 参数:连接的mysql对象
- 返回值:字段(列)数目
使用mysql_field_count判断mysql_store_result的调用失败原因
- mysql_field_count提供一个功能:判断为何mysql_ store_result的调用会失败
- 例如,如果mysql_store_result返回NULL,但是mysql_field_ count 返回一个正数,你可以推测这是一个提取错误。但是,如果mysql_field_count返回0,则表示没有 列可以提取,这可以解释为何存储结果会失败
演示案例
- 因为涉及到博主的数据库的密码,下面我们的mysql_real_connect函数的数据库密码使用“xxx”表示(出于隐私)
//insert4.c void display_show(MYSQL *mysql,MYSQL_ROW *sqlrow); int main(int argc,char *argv[]) { MYSQL mysql; MYSQL_RES *res; MYSQL_ROW sqlrow; mysql_init(&mysql); if(mysql_real_connect(&mysql,"localhost","root", "xxx","demo",0,NULL,0)){ printf("Connect success\n"); //select if(mysql_query(&mysql,"select childno,fname,age from children where age>5")!=0){ fprintf(stderr,"mysql_query failed:\n\tcode:%d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); }else{ //get result res=mysql_use_result(&mysql); if(res!=NULL){ //Get one row at a time while(sqlrow=mysql_fetch_row(res)){ printf("Fetch row:\n"); display_show(&mysql,&sqlrow); } }else{ if(mysql_errno(&mysql)){ printf("\terror code is %d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); } } } mysql_close(&mysql); }else{ fprintf(stderr,"Connect failed:\n"); if(mysql_errno(&mysql)){ printf("\terror code is %d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); } } return 0; } void display_show(MYSQL *mysql,MYSQL_ROW *sqlrow) { unsigned int count=0; while(count<mysql_field_count(mysql)){ printf("\t%s",(*sqlrow)[count]); count++; } printf("\n"); }
- 查看数据库:
- 编译程序:
gcc -I/usr/include/mysql select4.c -L/usr/lib/mysql -lmysqlclient -o select4
- 运行结果:因为查询的结果集中有3个字段,因此mysql_field_count函数返回3,在display_show函数中我们用while循环遍历,因为sqlrow代表一行数据,接着我们用索引一次取出一行中的0、1、2下标的数据,也就是childno、fname、age
十一、获得字段属性(mysql_fetch_field)
-
-
-
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result);
- 上面我们使用mysql_field_count函数虽然也可以打印数据,但是输出不美观,并且每回都返回一行数据,而且查询的结果中可以能会出现NULL。如果想要打印出更整洁的格式化(或许是表格化)的数据,你需要同时得到MySQL返回的数据和元数据。你可以使用mysql_fetch_field函数来同时将元数据和数据提取到一个新的结构中
- 参数:查询返回的结果集
- 返回值:
- 成功:返回一个MYSQL_FIELD的结构
- 数据读取完或失败:返回NULL
MYSQL_FIELD的结构
- 一个字段就用一个MYSQL_FIELD结构体标识,该结构体可以表示字段的数据类型、字段名、所属表等信息
typedef struct st_mysql_field { char *name; /* Name of column */ char *org_name; /* Original column name, if an alias */ char *table; /* Table of column if column was a field */ char *org_table; /* Org table name, if table was an alias */ char *db; /* Database for table */ char *catalog; /* Catalog for table */ char *def; /* Default value (set by mysql_list_fields) */ unsigned long length; /* Width of column (create length) */ unsigned long max_length; /* Max width for selected set */ unsigned int name_length; unsigned int org_name_length; unsigned int table_length; unsigned int org_table_length; unsigned int db_length; unsigned int catalog_length; unsigned int def_length; unsigned int flags; /* Div flags */ unsigned int decimals; /* Number of decimals in field */ unsigned int charsetnr; /* Character set */ enum enum_field_types type; /* Type of field. See mysql_com.h for types */ void *extension; } MYSQL_FIELD;比较重要的成员
- char *name:列名,为字符串
- char *table:列所属的表名。当一个查询要使用到多个表时,这将特别有用。注意: 对于结果中可计算的值如MAX,它所对应的表名将为空字符串
- char *def:如果调用mysql_list_fields(我们未在这里介绍它),它将包含该列 的默认值
- enum enum_field_types type:列类型,相当广泛,列类型在头文件mysql_con.h头文件中,常见的几种如下
- FIELD_TYPE_DECIMAL
- FIELD_TYPE_LONG
- FIELD_TYPE_STRING
- FIELD_TYPE_VAR_STRING
- unsigned int length:列宽,在定义表时指定
- unsigned int max_length:如果使用mysql_store_result,它将包含以字节为单位的提取的最长列 值的长度。如果使用mysql_use_result,它将不会被设置
- unsigned int flags:关于列定义的标志,与得到的数据无关。常见标志的含义:NOT_NULL_FLAG、PRI_KEY_FLAG、UNSIGNED_FLAG、AUTO_INCREMENT_FLAG和BINARY_FLAG。完整列表可参见MySQL文档。可以在编程时利用flags成员与这些值进行“&”然后某一字段是否属于某一标志
- unsigned int decimals:小数点后的数字个数。仅对数字字段有效
IS_NUM宏
- 功能:当字段类型为数字时,它返回true
//演示案例 if(IS_NUM(mysql_filed_prt->type)) printf("Number type field\n");
mysql_field_seek函数
MYSQL_FIELD_OFFSET mysql_field_seek(MYSQL_RES *result,MYSQL_FIELD_OFFSET offset);
- 设置当前的字段编号
- 该编号会随每次mysql_fetch_field调用而自动增加
- 如果给参数offset传递值0,你将跳回第一列
演示案例
- 因为涉及到博主的数据库的密码,下面我们的mysql_real_connect函数的数据库密码使用“xxx”表示(出于隐私)
//select5.c void display_header(MYSQL_RES *result); void display_show(MYSQL *mysql,MYSQL_ROW *sqlrow); int main(int argc,char *argv[]) { int isHeader; MYSQL mysql; MYSQL_RES *res; MYSQL_ROW sqlrow; isHeader=1; mysql_init(&mysql); if(mysql_real_connect(&mysql,"localhost","root", "xxx","demo",0,NULL,0)){ printf("Connect success\n"); //select if(mysql_query(&mysql,"select childno,fname,age from children where age>5")!=0){ fprintf(stderr,"mysql_query failed:\n\tcode:%d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); }else{ //get result res=mysql_use_result(&mysql); if(res!=NULL){ //Get one row at a time while(sqlrow=mysql_fetch_row(res)){ if(isHeader){ display_header(res); isHeader=0; printf("Row Deails:\n"); } display_show(&mysql,&sqlrow); } }else{ if(mysql_errno(&mysql)){ printf("\terror code is %d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); } } } mysql_close(&mysql); }else{ fprintf(stderr,"Connect failed:\n"); if(mysql_errno(&mysql)){ printf("\terror code is %d\n\treason:%s\n", mysql_errno(&mysql),mysql_error(&mysql)); } } return 0; } void display_header(MYSQL_RES *result) { printf("Column Details:\n"); MYSQL_FIELD *field; //循环,每次返回一个字段 while((field=mysql_fetch_field(result))!=NULL){ //1 printf("\tcol_name:%s",field->name); //2 if(IS_NUM(field->type)){ printf(" ,type:%s","Number"); }else{ switch(field->type){ case FIELD_TYPE_DECIMAL: printf(" ,type:%s","Decimal"); break; case FIELD_TYPE_LONG: printf(" ,type:%s","Long"); break; case FIELD_TYPE_STRING: printf(" ,type:%s","String"); break; case FIELD_TYPE_VAR_STRING: printf(" ,type:%s","Var_String"); break; } } //3 printf(" ,column width:%ld",field->length); //4 if(field->flags & AUTO_INCREMENT_FLAG){ printf( " ,column flags:%s","AUTO_INCREMENT_ FLAG"); } printf("\n"); } } void display_show(MYSQL *mysql,MYSQL_ROW *sqlrow) { unsigned int count=0; while(count<mysql_field_count(mysql)){ if((*sqlrow)[count]){ printf("\t%s",(*sqlrow)[count]); }else{ printf("NULL"); } count++; } printf("\n"); }
- 查看数据库:
- 编译程序:
gcc -I/usr/include/mysql select5.c -L/usr/lib/mysql -lmysqlclient -o select5
- 运行程序:
十二、更多的函数
- 下表中显示了其他一些其他常用的API函数
示例API调用 | 说 明 |
char *mysql_get_client_info(void); | 返回客户端使用的库的版本信息 |
char *mysql_get_host_info(MySQL *connection); | 返回服务器连接信息 |
char *mysql_get_server_info(MySQL *connection); | 返回当前连接的服务器的信息 |
char *mysql_info(MySQL*connection); | 返回最近执行的查询的信息,但是仅仅只对一些查询 类型有效——通常是INSERT和UPDATE语句,否则返回 NULL |
int mysql_select_db(MySQL *connection, const char *dbname); | 如果用户拥有合适的权限,则把默认数据库改为参数 指定的数据库。成功时返回0 |
int mysql_shutdown(MySQL *connection,enum mysql_enum_shutdown_level); | 如果用户拥有合适的权限,则关闭连接的数据库服务 器。目前关闭级别必须被设置为SHUTDOWN_DEFAULT。成 功时返回0 |
原文地址:https://www.cnblogs.com/ko88/p/11719312.html
- python使用rsa库做公钥解密(网上别处找不到)
- 通过“四大行为”对WCF的扩展[原理篇]
- WCF客户端运行时架构体系详解[下篇]
- WCF客户端运行时架构体系详解[上篇]
- WCF服务端运行时架构体系详解[续篇]
- [WCF-Discovery] 实例演示:如何利用服务发现机制实现服务的“动态”调用?
- [WCF-Discovery]服务如何能被”发现”
- 我的数据访问函数库的源代码(一)—— 共用部分
- 《WCF服务编程》关于“队列服务”一个值得商榷的地方
- 我的数据访问函数库的源代码(二)—— SQL语句部分
- 来源于WCF的设计模式:可扩展对象模式[上篇]
- 我的数据访问函数库的源代码(三)——返回结构数组
- 我的数据访问函数库的源代码(四)—— 存储过程部分,包括存储过程的参数的封装
- [WCF 4.0新特性] 路由服务[实例篇]
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- laravel-admin自动生成模块,及相关基础配置方法
- laravel-admin select框默认选中的方法
- Laravel-admin之修改操作日志的方法
- php使用curl伪造浏览器访问操作示例
- 关于laravel后台模板laravel-admin select框的使用详解
- 基于Laravel-admin 后台的自定义页面用法详解
- php解决约瑟夫环算法实例分析
- 浅谈laravel-admin的sortable和orderby使用问题
- 使用composer安装使用thinkphp6.0框架问题【视频教程】
- 基于laravel-admin 后台 列表标签背景的使用方法
- 解决laravel-admin 自己新建页面里 js 需要刷新一次的问题
- laravel-admin 中列表筛选方法
- Laravel框架控制器的middleware中间件用法分析
- laravel-admin的图片删除实例
- 在laravel-admin中列表中禁止某行编辑、删除的方法