简单尝试使用c语言的库对mysql的数据进行增删改查
刚开始数据库没有设定密码和用户,直接用root用户登录
mysql -uroot;
创建一个hello数据库,并进入这个数据库
create database hello; use hello;
创建一个stu_student数据表
create table stu_test( id int primary key auto_increment, name varchar(30), age int, score decimal(4,2) );
其中第一个id的类型是int,并设置为了auto_increment,每次插入数据的时候它都会自增。
name是字符串类型,age是int类型,score分数是浮点类型
decimal(4,2) 表示的范围是 -99.99 ~ 99.99 decimal(4,2)unsigned 表示的范围是 0 ~ 99.99
到这里,前期的准备就完成了
依照注释,简单认识一下mysql库的一些接口
//头文件 #include//Mysql操作句柄初始化 MYSQL *mysql_init(MYSQL *mysql); //参数为空则动态申请句柄空间进行初始化 //失败返回NULL //连接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 client_flag); //mysql--初始化完成的句柄 //host---连接的mysql服务器的地址 //user---连接的服务器的用户名 //passwd-连接的服务器的密码 //db ----默认选择的数据库名称 //port---连接的服务器的端口: 默认0是3306端口 //unix_socket---通信管道文件或者socket文件,通常置NULL //client_flag---客户端标志位,通常置0 //返回值:成功返回句柄,失败返回NULL //设置当前客户端的字符集 int mysql_set_character_set(MYSQL *mysql, const char *csname) //mysql--初始化完成的句柄 //csname--字符集名称,通常:"utf8" //返回值:成功返回0, 失败返回非0 //选择操作的数据库 int mysql_select_db(MYSQL *mysql, const char *db) //mysql--初始化完成的句柄 //db-----要切换选择的数据库名称 //返回值:成功返回0, 失败返回非0 //执行sql语句 int mysql_query(MYSQL *mysql, const char *stmt_str) //mysql--初始化完成的句柄 //stmt_str--要执行的sql语句 //返回值:成功返回0, 失败返回非0 //保存查询结果到本地 MYSQL_RES *mysql_store_result(MYSQL *mysql) //mysql--初始化完成的句柄 //返回值:成功返回结果集的指针, 失败返回NULL //获取结果集中的行数与列数 uint64_t mysql_num_rows(MYSQL_RES *result); //result--保存到本地的结果集地址 //返回值:结果集中数据的条数; unsigned int mysql_num_fields(MYSQL_RES *result) //result--保存到本地的结果集地址 //返回值:结果集中每一条数据的列数; //遍历结果集 MYSQL_ROW mysql_fetch_row(MYSQL_RES *result) //result--保存到本地的结果集地址 //返回值:实际上是一个char **的指针,将每一条数据做成了字符串指针数组 row[0]-第0列 row[1]-第1列 //并且这个接口会保存当前读取结果位置,每次获取的都是下一条数据 //释放结果集 void mysql_free_result(MYSQL_RES *result) //result--保存到本地的结果集地址 //返回值:void //关闭数据库客户端连接,销毁句柄: void mysql_close(MYSQL *mysql) //获取mysql接口执行错误原因 const char *mysql_error(MYSQL *mysql)
因为mysql是一个动态库,所以编译的时候,需要加上库名的链接
g++ test.cpp -o test -lmysqlclient
在我的系统下,MySQL的库是在/usr/lib64目录下的,所以我不需要额外指定库的路径。如果你的系统该目录下没有libmysqlclient.so ,则需要找到库安装的路径,使用-L命令指定该路径
g++ test.cpp -o test -L/path/to/mysql/lib -lmysqlclient
开始写代码,一步一步认识mysql的各个接口
因为我的数据库都是默认的环境,所以不需要账户的密码,按如下宏定义一下我们需要操作的数据库信息,方便后续的修改
#include#define HOST "127.0.0.1" #define PORT 3306 #define USER "root" #define PASSWD "" #define DBNAME "hello"
第一步,是用init来初始化一个MYSQL的结构体,并用一个指针来接收;
第二步,用mysql_real_connect函数来进行数据库的连接,填入我们刚刚的宏定义即可。
第三步,因为在最初配置mariadb的环境时,为了更好的支持中文,我们将数据库的字符集设置为了utf8,代码中也需要同步修改,避免因为编码问题产生的数据乱码
最后一步,销毁mysql结构体
int main() { // 连接数据库 // 初始化 MYSQL *mysql = mysql_init(nullptr); if (mysql == nullptr) // 返回值为空代表init失败 { cerr << "[ERR] init mysql handle failed!\n"; return -1; } // 连接 cout << "[INFO] connect to " << HOST << ":" << PORT << " " << USER << " " << DBNAME << endl; // 第一个参数为输出型参数。返回值为MYSQL的起始地址,如果错误返回NULL if (mysql_real_connect(mysql, HOST, USER, PASSWD, DBNAME, PORT, nullptr, 0) == nullptr) { cerr << "[ERR] mysql connect error: " << mysql_error(mysql) << endl; return -1; } // 配置为和数据库同步的utf8字符集 mysql_set_character_set(mysql, "utf8"); // 到这里就已经成功了 cout << "[INFO] mysql database connect success!" << endl; // 关闭连接 mysql_close(mysql); return 0; }
编译执行,成功链接上了数据库
$ make g++ test.cpp -o test -lmysqlclient $ ./test [INFO] connect to 127.0.0.1:3306 root hello [INFO] mysql database connect success!
下面要做的,就是给已有表新增一个键值
需要注意的是,mysql的代码操作,实际上也是需要使用sql语句的(这点我觉得好麻烦)
插入语句如下,括号中的键值应该和数据库中表的键值一一对应
insert into stu_test value (null,'牛爷爷',50,64.6);
所以我们要做的,就是写一个函数,将传入的参数合并成一个sql语句,传入mysql进行处理
// 将double转为string std::string double2string(const double& d) { std::stringstream s_tmp; s_tmp << d; std::string s = s_tmp.str(); return s; } int add_key_to_stu(MYSQL *mysql,const std::string& name,int age,double score) { // 1.将传入的参数处理为一个完整的sql语句 // 因为第一个编号参数,配置的是自增,所以需要传入null std::string sql_cmd = "insert into stu_test value (null,'"; sql_cmd+= name; sql_cmd+= "',"; sql_cmd+= std::to_string(age); sql_cmd+= ","; sql_cmd+= double2string(score); sql_cmd+= ");"; cout << "[INFO] " << sql_cmd << endl; // 2.执行语句 int ret = mysql_query(mysql,sql_cmd.c_str()); if(ret!=0) { cerr << "[ERR] mysql insert error: " << mysql_error(mysql) << endl; } return ret; }
如下代码测试
// 添加一个数据 add_key_to_stu(mysql,"牛爷爷",50,64.6); add_key_to_stu(mysql,"小图图",5,72.8);
可以看到,成功运行!
$ ./test [INFO] connect to 127.0.0.1:3306 root hello [INFO] mysql database connect success! [INFO] insert into stu_test value (null,'牛爷爷',50,64.6); [INFO] insert into stu_test value (null,'小图图',5,72.8);
进入mysql命令行,使用如下命令,即可查询到已有的键值
select * from stu_test;
可以看到,处理成功!
+----+-----------+------+-------+ | id | name | age | score | +----+-----------+------+-------+ | 1 | 牛爷爷 | 50 | 64.60 | | 2 | 小图图 | 5 | 72.80 | +----+-----------+------+-------+ 2 rows in set (0.001 sec)
sql语句如下,修改当前数据库中,stu_test表中name为牛爷爷的条目的成绩为70
update stu_test set score=70 where name='牛爷爷';
// 修改已有学生的成绩 int mod_score_in_stu(MYSQL *mysql,const std::string& name,double score) { std::string sql_cmd = "update "; sql_cmd += TABLENAME; sql_cmd += " set score="; sql_cmd += double2string(score); sql_cmd += " where name='"; sql_cmd += name; sql_cmd += "';"; cout << "[INFO] " << sql_cmd << endl; // 2.执行语句 int ret = mysql_query(mysql,sql_cmd.c_str()); if(ret!=0) { cerr << "[ERR] mysql mod_score error: " << mysql_error(mysql) << endl; } return ret; }
可以看到,执行成功了之后,牛爷爷的成绩发生了变化
$ ./test [INFO] connect to 127.0.0.1:3306 root hello [INFO] mysql database connect success! [INFO] update stu_test set score=70 where name='牛爷爷';
MariaDB [hello]> select * from stu_test; +----+-----------+------+-------+ | id | name | age | score | +----+-----------+------+-------+ | 1 | 牛爷爷 | 50 | 64.60 | | 2 | 小图图 | 5 | 72.80 | +----+-----------+------+-------+ 2 rows in set (0.001 sec) MariaDB [hello]> select * from stu_test; +----+-----------+------+-------+ | id | name | age | score | +----+-----------+------+-------+ | 1 | 牛爷爷 | 50 | 70.00 | | 2 | 小图图 | 5 | 72.80 | +----+-----------+------+-------+ 2 rows in set (0.000 sec)
如果想修改其他键值,也是依照此步骤进行。
在实际场景中,一个数据条目肯定会有一个键值是不变的。比如在我的学生表中,假定每一个学生的名字都不变,我们就可以根据name字段来查找键值,对之进行其他值的修改。
如果真要严肃起来,用名字来查找肯定是不行的,我们应该给每一个学生都生成一个学号或UID,用这个id来进行检索。修改数据之前,都需要知道目标数据的uid值,才能修改。
如下语句,在test_tb表中,删除名字为张三的键值
delete from test_tb where name='张三';
在删除之前,我又给数据库新增了一个键值
MariaDB [hello]> select * from stu_test; +----+-----------+------+-------+ | id | name | age | score | +----+-----------+------+-------+ | 1 | 牛爷爷 | 50 | 70.00 | | 2 | 小图图 | 5 | 72.80 | | 3 | 大司马 | 42 | 87.30 | +----+-----------+------+-------+ 3 rows in set (0.001 sec)
下面我要用代码,删除掉牛爷爷
// 删除键值(根据名字) int del_key_in_stu(MYSQL *mysql,const std::string& name) { std::string sql_cmd = "delete from "; sql_cmd += TABLENAME; sql_cmd += " where name='"; sql_cmd += name; sql_cmd += "';"; cout << "[INFO] " << sql_cmd << endl; // 执行语句 int ret = mysql_query(mysql,sql_cmd.c_str()); if(ret!=0) { cerr << "[ERR] mysql mod_score error: " << mysql_error(mysql) << endl; } return ret; }
// 删除已有键值 del_key_in_stu(mysql,"牛爷爷");
编译执行
$ ./test [INFO] connect to 127.0.0.1:3306 root hello [INFO] mysql database connect success! [INFO] delete from stu_test where name='牛爷爷';
牛爷爷被删掉了
MariaDB [hello]> select * from stu_test; +----+-----------+------+-------+ | id | name | age | score | +----+-----------+------+-------+ | 2 | 小图图 | 5 | 72.80 | | 3 | 大司马 | 42 | 87.30 | +----+-----------+------+-------+ 2 rows in set (0.000 sec)
此时再插入一个新的键值,可以看到id并么有把空余的1给补上,而是从4开始继续往后增加
MariaDB [hello]> select * from stu_test; +----+-----------+------+-------+ | id | name | age | score | +----+-----------+------+-------+ | 2 | 小图图 | 5 | 72.80 | | 3 | 大司马 | 42 | 87.30 | | 4 | 乐迪 | 32 | 99.00 | +----+-----------+------+-------+ 3 rows in set (0.001 sec)
知道这一点就行
对于数据库而言,查询也是一个高频操作
select * from tb where name=key;
上面的语句,是在数据库表tb中查找键值name为key的的数据
// 返回用户的所有信息,name为空返回所有 void get_all_in_stu(MYSQL *mysql,const std::string& name="") { std::string sql_cmd = "select * from "; sql_cmd += TABLENAME; if(name.size()!=0) { sql_cmd += " where name='"; sql_cmd += name; sql_cmd += "'"; } sql_cmd += ";"; cout << "[INFO] " << sql_cmd << endl; int ret = mysql_query(mysql,sql_cmd.c_str()); if(ret!=0) { cerr << "[ERR] mysql query error: " << mysql_error(mysql) << endl; return ; } // 获取结果 MYSQL_RES *res = mysql_store_result(mysql); if (res == nullptr) { cerr << "[ERR] mysql store_result error: " << mysql_error(mysql) << endl; return ; } int row = mysql_num_rows(res); // 行 int col = mysql_num_fields(res); //列 printf("%10s%10s%10s%10s\n", "ID", "姓名", "年龄", "成绩"); for (int i = 0; i < row; i++) { MYSQL_ROW row_data = mysql_fetch_row(res); for (int i = 0; i < col; i++) { printf("%10s", row_data[i]); } printf("\n"); } // 释放结果 mysql_free_result(res); }
get_all_in_stu(mysql);
执行结果如下,显示出了表中所有的值
[INFO] connect to 127.0.0.1:3306 root hello [INFO] mysql database connect success! [INFO] select * from stu_test; ID 姓名 年龄 成绩 2 小图图 5 72.80 3 大司马 42 87.30 4 乐迪 32 99.00
传入特定名字,则只返回改名字所有值
get_all_in_stu(mysql,"乐迪");
[INFO] select * from stu_test where name='乐迪'; ID 姓名 年龄 成绩 4 乐迪 32 99.00
这样便实现了查询操作。
除了使用name=,还可以使用name like %名字%这条语句进行模糊匹配,即不在乎名字前后的内容,只要有包含名字的数据,就筛选出来。
select * from %s where name like '%牛%';
这样就能将名字里面包含牛的所有数据都筛选出来
因为查询的返回值是一个字符串二维数组,并不能通过字段名字获取到对应字段的内容。这要求程序猿知晓这个表中字段的顺序,才能通过下标获取到正确的字段对其进行处理。
因为cpp并不像python那样支持可变变量类型,所以要想筛选出对应类型的数据,还需要我们自行调用对应的函数进行数据的转换。
比如用atoi函数将字符串转为整型。
基本的操作到这里就OVER了,后续的其他操作会更新本博客