C语言下使用MySQL数据库提供的API操作数据库。建议结合查看API官方文档,开发环境搭建过程如下:
1. 安装MySQL数据库。(你的项目与MySQL Lib库位数必须一致,否则不兼容,比如你编写的程序是32位的就安装32位的MySQL数据库,64位就安装64位的MySQL数据库)如何安装这里就不介绍了,MySQL5.4版本后的都可以,对API没影响。
2. 在MySQL安装路径下找到lib文件中的libmysql.lib、libmysql.dll,复制到项目路径下,然后同样把MySQL安装路径下的include文件中的mysql.h复制到项目路径下。在项目头文件中添加库与头文件:
#include "mysql.h"
#pragma comment(lib,"libmysql.lib")
然后就可以愉快的使用C操作数据库啦!主要内容如下:
1. 配置与连接数据库。
2. 写数据库
3. 读数据库
const char host[] = "localhost"; //数据库ip地址,这里是本地数据库。
const char name[] = "root"; //数据库用户名。
const char passwd[] = "123"; //数据库密码。
const char db[] = "hgdb"; //数据库名。
MYSQL m_sqlCon; //全局变量,数据库的句柄。
mysql_init(&m_sqlCon); //初始化数据库。
my_bool mybool = true; //typedef char my_bool,其实就是数据库的bool类型
mysql_options(&m_sqlCon, MYSQL_OPT_RECONNECT, &mybool); //开启自动重连(注意自动重连也是要调用mysql_ping()函数才会重连的),mysql_options函数可以配置数据库各种参数,比如传输的包大小,开启多SQL语句执行,读写超时时间等,总之可以优化数据库。
if (mysql_real_connect(&m_sqlCon, host, name, passwd, db, 3306, NULL, CLIENT_MULTI_STATEMENTS)) //连接数据库。3306是数据库端口号,CLIENT_MULTI_STATEMENTS为开启多SQL语句执行。
else
{
switch(mysql_errno(&m_sqlCon)) //连接数据库失败,查看错误信息。这里只列出了三个错误信息。
{
case CR_CONNECTION_ERROR: break; //Failed to connect to the MySQL server.
case CR_UNKNOWN_HOST: break; //Failed to find the IP address for the host name.
case CR_VERSION_ERROR: break; //A protocol mismatch resulted from attempting to connect to a server with a client library that uses a different protocol version.
default: break;
}
}
要说明的都在注释中了。MySQL API提供了两种方式操作数据库,一种是mysql_real_query()函数执行SQL语句,另外一种就是prepared statement方式了。第一种方式使用起来很简单,就是把字符串类型SQL语句作为参数调用mysql_real_query()函数执行就行了。prepared statement方式对于频繁操作数据库有优势,具体使用哪种根据实际效率看吧。以下操作都会介绍两种方式。
第一种方式:
string sql = "insert into `tableName` values(1, '2', ...)"; //就是简单的SQL语句而已,但是这里有两点要注意!表名两边加上`符号(Tab键上面的那个键也就是1键左边的键),字段值是字符或字符串类型的话两边要加'符号。
if(mysql_real_query(&m_sqlCon, sql.c_str(), sql.length())!=0) //执行SQL语句,一个函数就实现了各种操作。执行成功返回0。
{
unsigned int errCode = mysql_errno(&m_sqlCon); //查看错误信息
if(errCode==2014)
{
//Commands were executed in an improper order.
}
else if(errCode==2006)
{
//The MySQL server has gone away.;
}
else if(errCode==2013)
{
//The connection to the server was lost during the query.;
}
else if(errCode==2000)
{
//An unknown error occurred.;
}
mysql_ping(&m_sqlCon); //检查数据库是否断开,如果断开就重连并且执行上一条出错的SQL语句。
}
第一种方式太简单了,直接调用mysql_real_query()执行各种SQL语句。但是要注意两点,SQL语句中表名两边加上`符号(Tab键上面的那个键也就是1键左边的键),字段值是字符或字符串类型的话两边要加’符号。
prepared statement方式
对于频繁执行的SQL语句,绑定到MYSQL_STMT类型变量中,类似预编译一样,其中原理查看API文档说明吧。首先调用mysql_stmt_init()函数申请MYSQL_STMT类型变量,然后调用mysql_stmt_prepare()函数绑定SQL语句,需要带值的SQL语句比如insert操作还需要调用mysql_stmt_bind_param()函数绑定值。字段值信息由MYSQL_BIND结构体存储。过程如下:
MYSQL_STMT *pstmt = mysql_stmt_init(&m_sqlCon); //全局变量。使用完后记得调用mysql_stmt_close()释放!
string stmtSql = "insert into `tableName` values(?,?,?,?)"; //SQL语句。
mysql_stmt_prepare(pstmt, stmtSql.c_str(), stmtSql.length()); //绑定SQL语句到MYSQL_STMT变量中,也就是之后再执行该SQL操作直接用MYSQL_STMT变量就行了,不用再重复写该SQL语句了。类似预编译一样。
MYSQL_BIND bindStmt[4]; //insert操作,要插入几个字段值数组就多大。这里4个字段值。MYSQL_BIND结构体存储字段的一些信息,比如值,是否为非负,非空等。
char filed1 = 'a';
int filed2 = 1;
float filed3 = 2.2;
char filed4[] = "123";
memset(bindStmt, 0, sizeof(bindStmt)); //初始化为0
bindStmt[0].buffer_type = MYSQL_TYPE_TINY; //char字段类型。
bindStmt[0].buffer = &filed1; //注意赋的是地址而不是值。
bindStmt[0].buffer_length = sizeof(filed1); //buffer的字节个数
bindStmt[1].buffer_type = MYSQL_TYPE_LONG; //int类型
bindStmt[1].buffer = &filed2;
bindStmt[1].buffer_length = sizeof(filed2);
bindStmt[2].buffer_type = MYSQL_TYPE_FLOAT;
bindStmt[2].buffer = &filed3;
bindStmt[2].buffer_length = sizeof(filed3);
bindStmt[3].buffer_type = MYSQL_TYPE_STRING;
bindStmt[3].buffer = filed4;
bindStmt[3].buffer_length = sizeof(filed4);
mysql_stmt_bind_param(pstmt, bindStmt); //绑定字段值到MYSQL_STMT中。
if(mysql_stmt_execute(pstmt) != 0) //执行SQL操作。
{
unsigned int errCode = mysql_errno(&m_sqlCon); //查看错误信息。
if(errCode==2014)
{
//Commands were executed in an improper order.
}
else if(errCode==2006)
{
//The MySQL server has gone away.
}
else if(errCode==2013)
{
//The connection to the server was lost during the query.;
}
else if(errCode==2000)
{
//An unknown error occurred.;
}
mysql_ping(&m_sqlCon);
}
调用mysql_stmt_init()申请的MYSQL_STMT变量用完后记得调用mysql_stmt_close()释放,不要用一次释放一次,应该等不需要再执行该SQL操作时再释放。有多少需要频繁操作的SQL语句就可以申请多少MYSQL_STMT变量。这种方式不能在一条语句中执行多SQL操作。MYSQL_BIND结构体主要用到的属性有:
struct MYSQL_BIND
{
enum enum_field_types buffer_type; //字段值类型,具体类型查看API文档。
void *buffer; //需要赋值的变量地址。
unsigned long buffer_length; //buffer指向变量的字节个数。
unsigned long *length; //指向实际字节个数。
my_bool *is_null; //是否为空。
my_bool is_unsigned; //是否为非负数。
......
}
第一种方式
string sql = "select * from `" + tableName + "`"; //SQL语句,读取表中所有数据。
mysql_real_query(&m_sqlCon, sql.c_str(), sql.length()); //执行SQL语句。
MYSQL_RES *sqlRes = mysql_store_result(&m_sqlCon); //查询结果集。还可以使用mysql_use_result(&m_sqlCon);
if (sqlRes)
{
MYSQL_ROW sqlRow; //typedef char **MYSQL_ROW,就是个二维字符数组,所以读取的字段值都是字符串形式的。
int colums = mysql_num_fields(sqlRes); //列数
while (sqlRow = mysql_fetch_row(sqlRes)) //每次取出一行数据。
{
for(int i=0; i
常用的查询结果集是mysql_store_result(),可以调用mysql_num_rows()获取行数,并且可以调用mysql_data_seek()或mysql_row_seek()改变当前行,缺点是需要足够的内存。如果查询的数据量很大,建议使用mysql_use_result(),每次只查询一行数据,所以占用内存少,但是缺点是不能获取行数,并且必须读取完所有行。
prepared statement方式
MYSQL_STMT *stmt;
int num_fields; //列数。
MYSQL_FIELD *fields; //列字段信息。
MYSQL_BIND *rs_bind; //存储字段值信息。
int data1; //存储字段值。
float data2; //存储字段值。
char data3[20]; //存储字段值。
stmt = mysql_stmt_init(&m_sqlCon);
string sql = "select * from `" + tableName + "`"; //SQL语句,读取表中所有数据。
mysql_stmt_prepare(stmt, sql.c_str(), sql.length()); //绑定SQL语句。
mysql_stmt_execute(stmt) ; //执行SQL语句。
num_fields = mysql_stmt_field_count(stmt); //获取列数。
if (num_fields > 0)
{
MYSQL_RES *rs_metadata = mysql_stmt_result_metadata(stmt); //获取结果集。
fields = mysql_fetch_fields(rs_metadata); //获取所有字段信息。可以编写万能查询操作,类似反射。
rs_bind = (MYSQL_BIND *) malloc(sizeof (MYSQL_BIND) * num_fields); //根据字段值个数开辟MYSQL_BIND空间。
memset(rs_bind, 0, sizeof (MYSQL_BIND) * num_fields);
for (int i = 0; i < num_fields; ++i) //根据字段信息设置MYSQL_BIND。
{
rs_bind[i].buffer_type = fields[i].type; //字段类型。
switch (fields[i].type)
{
case MYSQL_TYPE_LONG: //整型。
rs_bind[i].buffer = (char *) &(data1); //字段值存储在data1中。
rs_bind[i].buffer_length = sizeof (data1);
break;
case MYSQL_TYPE_FLOAT: //单精度浮点型float。
rs_bind[i].buffer = (char *)&(data2); //字段值存储在data2中。
rs_bind[i].buffer_length = sizeof (data2);
break;
case MYSQL_TYPE_CHAR: //字符串。
rs_bind[i].buffer = data3; //字段值存储在data3中。
rs_bind[i].buffer_length = sizeof (data3);
break;
default:
break;
}
}
mysql_stmt_bind_result(stmt, rs_bind); //绑定MYSQL_BIND。
while (mysql_stmt_fetch(stmt)) //每次获取一行。
{
//data1,data2,data3存储了字段值。
}
mysql_free_result(rs_metadata); //释放结果集。
free(rs_bind); //释放MYSQL_BIND空间。
}
除了使用API操作MYSQL外,还可以使用MYSQL Connector/C,具体信息就去看MYSQL官方文档吧。