2018-06-08
最近想学习一下mysql,看了一下书《MySQL必知必会》, 感觉书讲的还不错,也不是很厚,260页,大略的翻完了,然后自己也下载了mysql安装了,实际操作了一下,感觉书里面前2章内容略微陈旧了,
简单的管理数据库,查询可以直接安装mysql workbench,感觉界面挺不错,也很好用。那个 mysql shell似乎可以甩在一边不用了。
对着书大概了用了一些mysql 语句,对mysql有点认识后,打算用c语言来访问mysql服务器,进行一些操作。找了一下,看见有一些库給不同的语言用,来和mysql交互。比如c语言可以用 mysql connector/c 库。
现在记录一下我在windows系统下,利用visual stdudio 2015 ,用c语言 访问mysql数据库,进行了一个简单查询语句的整个过程。
工程代码如下:
1 #include
2
3 #include
4
5 #pragma comment(lib, "ws2_32.lib")
6 #pragma comment(lib, "Secur32.lib")
7 #pragma comment(lib, "mysqlclient.lib")
8 //#pragma comment(lib, "libmysql.lib") 使用这个总是会报错, lnk1107 文件无效或损坏,无法在0x348处读取
9
10 #define LOG(...) fprintf(stderr, __VA_ARGS__)
11
12 int main(int argc, char*argv[])13 {14 MYSQL* pmysql =mysql_init(NULL);15 if (pmysql ==NULL)16 {17 LOG("初始化mysql库失败\n");18 return -1;19 }20
21 MYSQL* ret = mysql_real_connect(pmysql, "localhost","test","test*","world",0,NULL,0);22 if (ret ==NULL)23 {24 LOG("连接到数据库 %s:%s 失败\n","localhost","world");25 return -1;26 }27
28 int ret_query = mysql_query(pmysql,"SELECT * FROM world.city;");29 if (ret_query == 0)30 {31 MYSQL_RES * my_result =mysql_store_result(pmysql);32
33 int num =mysql_num_fields(my_result);34 inti;35 MYSQL_ROW row;36 while ((row = mysql_fetch_row(my_result)) !=NULL)37 {38 for (i = 0; i < num; i++)39 {40 printf("%s\t", row[i]);41 }42 printf("\n");43 }44
45 //释放获取的结果
46 mysql_free_result(my_result);47 }48
49 mysql_close(pmysql);50 mysql_library_end();51 return 0;52 }
完整的工程可以到我的github上下载
https://github.com/fish404/mysql_learning
编译时候有几个地方要注意:
1、需要指定mysql.h 所在的头文件的路径,vs 在项目属性中设置。项目属性页 --》 c/c++ --》常规 --》附加包含目录中指定,最好用双引号将路径包含起来,如我的路径是 "C:\Program Files (x86)\MySQL\MySQL Connector C 6.1\include"。
2、需要指定 mysqlclient.lib 所在的库路径,注意这个路径跟你的vs 的版本有关,比如我的是 vs 2015,连接的库版本也要是2015编译出来的,对应的connector lib目录下 vs14目录,路径是 “C:\Program Files (x86)\MySQL\MySQL Connector C 6.1\lib\vs14”。
3、不要直接在属性也中指定依赖的库,否则连接的时候总是报错,搜了一下不知道是什么原因,最后用代码文件开头 #pragma comment(lib, "mysqlclient.lib") 编译通过。
4、还需要依赖 另外两个库 ws2_32.lib 和 Secur32.lib,跟上一点一样指定。
5、vs 配置管理器中选择x86,则也要安装 mysql connector/c 的x86版本,前面1,2包含的路径也要是x86版本的路径。x64版本的不知道为何,总是报错。
6、连接静态库 mysqlclient.lib 可以运行,但是用动态库libmysql.dll 总是失败。暂时没有深究原因。
参考文档:
1、https://dev.mysql.com/doc/refman/5.6/en/c-api-function-overview.html 概述说明
2、https://dev.mysql.com/doc/refman/5.6/en/c-api-functions.html 各个函数详细参数说明,返回值意义等
3、https://www.codeproject.com/Articles/34646/Accessing-MySQL-data-base-using-MySQL-C-API, 别人写的一个示例,用c++包装了。
下面一段是对参考文档1 的翻译,大概是讲了使用的步骤,一些注意事项。
连接的库
libmysqlclient 或者 libmysqld
mysql_library_init() 初始化mysql客户库 。这个函数不是线程安全,多线程情况下一定要先调用这个函数之后,再生成线程,调用其它库函数。
mysql_init() 初始化一个连接处理器。 会间接调用mysql_library_init函数,非多线程环境下,可以省略上面这个函数。
mysql_real_connect() 连接到服务器
函数原型是 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 client_flag)
mysql_close() 关闭到服务器的连接
mysql_library_end() 用完了mysql库后需要释放,不然有些内存会保持分配,让一些工具软件抱怨。
连接成功后,可以调用下面两个函数来执行sql语句。
mysql_query() 期望查询语句是null结尾。 每一次调用这个查询后,接着调用mysql_store_result 或者 mysql_use_result来处理结果。
mysql_real_query() 如果字符串中有二进制的数据,必须使用这个函数。
sql语句有select语句,非select语句(如insert,update,delete)。对非select语句,
可以调用 mysql_affected_rows() 得到有多少行收到影响。
对select语句(包括select,show,describe,explain),你将获取到选中的行作为结果集合。
有2种方式处理结果集合。
一个是mysql_store_result(),这个函数一次性获取所有的选中的行,保存到客户处。这种方式用的比较多,因为你可以顺序访问所有的行,并且可以前后移动行,通过mysql_row_seek() 或者 mysql_data_seek() 来改变结果集合中的当前行位置。
还可以通过mysql_num_rows()知道一共有多少行返回了。
这个方式缺点是可能如果返回结果太多,内存可能不够,而且等待时间可能较大。
另一个是 mysql_use_result(),这个函数只是初始化获取,但是实际上还没有从服务器获取任意一行数据。
上面任意一种方式,都可以调用mysql_fetch_row() 来访问获取的结果的一行。对第二种方式,该函数实际是从服务器获取数据。
mysql_fetch_field() 可以用来获取各个字段信息,通过在一行中连续调用。也可以通过mysql_fetch_field_direct() 指定字段号来获取字段信息。mysql_field_seek() 可以改变当前字段游标位置。
mysql_fetch_fields() 可以一次获取所有的字段信息。
mysql_fetch_lengths() 可以获取每行的长度信息。
mysql_free_result() 用完结果后释放内存。
关于mysql_store_result() 还有一点补充,如果这个函数返回成功,那么sql语句是select语句。如果失败,进一步调用 mysql_field_count() 来判断结果是否符合预期,如果这个函数返回0,说明sql语句不需要返回语句,以为着这是一个insert,update,
delete语句。如果不为0,则说明它期待返回数据,sql语句是select语句,并且失败了。