Linux下MySQL的简单使用

Linux下MySQL的简单使用

导语

这一章是MySQL的使用,一些常用的MySQL语句属于本科阶段内容,然后是C语言和MySQl之间的交互,可以看到C语言已经有成熟的和MySQL的接口

MySQL安装与配置

MySQL安装

MySQL的安装很简单,只需要执行几个shell命令就行,需要注意的是,对于Ubuntu新机器来说最好不要一开始就执行sudo apt update,因为很多包之间的依赖是复杂且未知的,如果直接全部更新可能会导致在跟书上同步实验时出问题(命令参考了ubuntu 23.10.1 mysql 安装

sudo apt install mysql-server -y#这里就安装好了服务器
systemctl start mysql#开启mysql服务
systemctl status mysql#检查状态,如果出现mysql>就没问题

sudo mysul -u root mysql#进入mysql
ps -el | grep mysqld#可以看mysql是否启动

下面这个是备选方案,是在新机器上装的

sudo apt-get update
sudo apt-get install libmysqlclient-dev
mysql_config --cflags
mysql_config --libs

后两条命令是检测MySQL Connector/C是否安装完成,由于新版本的MySQL已经不支持书上的写法,所以需要安装这个包,正常应该显示如下图

之后直接安装服务器即可

sudo apt-get update
sudo apt-get install mysql-server

通过下面的程序验证安装是否成功

#include <stdio.h>
#include <mysql/mysql.h>

int main() {
    MYSQL *conn;
    conn = mysql_init(NULL);

    if (conn == NULL) {
        fprintf(stderr, "mysql_init() failed
");
        return EXIT_FAILURE;
    }

    if (mysql_real_connect(conn, "localhost", "user", "password", "database", 0, NULL, 0) == NULL) {
        fprintf(stderr, "mysql_real_connect() failed: %s
", mysql_error(conn));
        mysql_close(conn);
        return EXIT_FAILURE;
    }

    printf("Connected to the database successfully!
");

    mysql_close(conn);
    return EXIT_SUCCESS;
}

执行的命令和结果如下

gcc -o test_program test_program.c $(mysql_config --cflags) $(mysql_config --libs)

密码设置

一开始的root是没有密码的,可以通过命令查看各用户的密码如下,可以看到root密码为空

这里需要注意的是新版的MySQL和老版的不一样,新版本的password列改成了authentication_string

可以执行命令来设置密码,修改后查看,可以看到root有密码,但是被MySQL加密过了,再次登录时直接回车发现无法识别,只有输入密码才能识别用户

这里我们删除除去root之外的默认用户,并且删除从localhost以外任何主机的登录

到此为止,我们有了一个运行的MySQL,该MySQL只有设定密码的root通过本地才能连接,为了方便后续的操作,这里创建一个普通用户rick,它将能用三种方法来连接MySQL(本地连接,从IP连接,从wiley.com域中任何机器连接)

首先为rick创建一个本地登录,这里书上给的代码太老了,已经不适配现在的MySQL,更改成了现在版本的,最新的 MySQL 版本中,GRANT 语句不再用于设置用户密码。密码应该通过单独的 CREATE USER 或 SET PASSWORD 语句来设置。


通过三种不同的方式设置了三个用户,可以通过select命令展示,可以看到三种方式都成功了,并且rick的权利很大

MySQL管理

命令

所有命令都接受三个标准参数

-u 用于指定用户(用户名)

-p 用于提示密码(如果在命令行中未直接提供)

-h 用于指定主机(MySQL 服务器地址)

除了标准参数之外,每个命令还有自己的命令选项

myisamchk

通常情况下,myisamchk以创建的mysql用户运行并且工作目录为数据表在的目录中,常见的命令选项有

-c 检查表以发现错误

-e 执行扩展检查

-r 修复发现的错误

mysql

使用mysql可以不经过MySQL的控制台完成很多功能,通过在命令行最后添加数据库名称作为参数即可,可以通过 mysql --help | less来查看所有的命令行选项列表

其他

还有一些其他的命令,如mysqladmin、mysqlbug等,由于MySQL已经相对于书上更新很多了,所以很多命令已经过时,在此不多赘述

常见操作

书上提供的常见操作包括grant、revoke以及数据类型等,这些属于本科阶段SQL的基本内容,在此不再赘述,跳过

C语言访问MYSQL

连接例程

C语言连接MySQL需要先初始化一个连接句柄结构,然后再进行实际的连接,句柄通过mysql_init函数初始化,函数原型具体如下,mysql_init() 函数通常是在使用 MySQL 客户端 API 进行数据库编程时的第一个步骤。它为连接操作做了准备,确保在进行连接操作之前,连接对象处于一个已初始化的状态

MYSQL *mysql_init(MYSQL *);
//mysql:一个指向 MYSQL 结构体的指针。如果该指针为空,
//mysql_init() 将分配一个新的 MYSQL 结构体并返回它。如果提供了一个非空指针,它会用新的数据初始化该对象

真正与数据库进行连接的是mysql_real_connect函数,函数原型如下

MYSQL *mysql_real_connect(MYSQL *mysql, 
                          const char *host, 
                          const char *user, 
                          const char *passwd, 
                          const char *dbname, 
                          unsigned int port, 
                          const char *unix_socket, 
                          unsigned long client_flag);
//mysql是初始化之后的句柄

还有例程函数和关闭函数,在此不赘述

现在尝试创立一个数据库,向其中填入数据并通过C语言访问它

逐行输入对数据库的操作比较麻烦,并且容易出错,可以把要执行的命令单独做成文件,然后直接导入执行

-- Create the table children

CREATE TABLE children (
    childno int(11) NOT NULL auto_increment,
    fname varchar(30),
    age int(11),
    PRIMARY KEY (childno)
);

-- Populate the table 'children'

INSERT INTO children (childno, fname, age) VALUES (1, 'Jenny', 21);
INSERT INTO children (childno, fname, age) VALUES (2, 'Andrew', 17);
INSERT INTO children (childno, fname, age) VALUES (3, 'Gavin', 8);
INSERT INTO children (childno, fname, age) VALUES (4, 'Duncan', 6);
INSERT INTO children (childno, fname, age) VALUES (5, 'Emma', 4);
INSERT INTO children (childno, fname, age) VALUES (6, 'Alex', 15);
INSERT INTO children (childno, fname, age) VALUES (7, 'Adrian', 9);


可以看到数据都被成功插入了

之后可以通过上述函数来连接数据库,由于系统和包之间的依赖问题以及mysql的版本问题,导致这个程序无法在旧机上运行,暂时没有找到解决方案,在新机上可以运行,运行结果和程序如下

#include <stdlib.h>
#include <stdio.h>
#include <mysql/mysql.h>  // 包含新版 MySQL Connector/C 的头文件

int main(int argc, char *argv[])
{
    MYSQL *con;

    // 初始化 MySQL 连接句柄
    con = mysql_init(NULL);
    if (con == NULL) {
        fprintf(stderr, "mysql_init() failed
");
        return EXIT_FAILURE;
    }

    // 连接数据库
    if (mysql_real_connect(con, "localhost", "rick", "111111", "foo", 0, NULL, 0) == NULL) {
        fprintf(stderr, "mysql_real_connect() failed
");
        fprintf(stderr, "Error: %s
", mysql_error(con));
        mysql_close(con);
        return EXIT_FAILURE;
    }

    // 输出连接成功状态
    printf("Connected successfully!
");

    // 关闭连接
    mysql_close(con);
    return EXIT_SUCCESS;
}

执行的时候需要添加对应的inlcude路径和库文件路径

错误处理

MySQL使用一系列由连接句柄结构报告的返回码来获取错误信息,具体的函数原型如下

unsigned int mysql_errno(MYSQL *connection);
//返回错误码
char *mysql_error(MYSQL *connection);
//返回有意义的文本信息

这里是书上给出的一个例子以及运行结果,可以看到屏幕上输出了错误信息

#include <stdio.h>
#include <stdlib.h>
#include <mysql.h>

int main(int argc, char *argv[]) {
    MYSQL my_connection;

    mysql_init(&my_connection);
    if (mysql_real_connect(&my_connection, "localhost", "rick", "I do not know", "foo", 0, NULL, 0)) {
        printf("Connection success
");
        mysql_close(&my_connection);
    } else {
        fprintf(stderr, "Connection failed
");
        if (mysql_errno(&my_connection)) {
            fprintf(stderr, "Connection error %d: %s
",
                    mysql_errno(&my_connection), mysql_error(&my_connection));
        }
    }
    return EXIT_SUCCESS;
}

使用SQL

这里直接给出一些C语言执行SQL语句的函数原型和简单解释

int mysql_query(MYSQL *connection, const char *query);
//第一个是初始化后的句柄,第二个是sql语句,返回错误码
my_ulonglong mysql_affected_rows(MYSQL *connection);
//查询上一条指令运行后影响了多少行

书上给出了一个使用AUTO_INCREMENT的代码实例,下面是修改后的程序和运行结果,AUTO_INCREMENT对每次插入的数据分配一个新的id值并进行追踪

#include <stdio.h>
#include <stdlib.h>
#include <mysql.h>

#define DB_HOST "localhost"
#define DB_USER "rick"  // 用户名 'rick'
#define DB_PASS "111111" // 数据库密码
#define DB_NAME "foo" // 数据库名称 'foo'

// 错误处理函数,减少冗余代码
void handle_error(MYSQL *connection, const char *message) {
    fprintf(stderr, "%s
Error %d: %s
", message, mysql_errno(connection), mysql_error(connection));
    mysql_close(connection);
    exit(EXIT_FAILURE);
}

int main(int argc, char *argv[]) {
    MYSQL my_connection;
    MYSQL_RES *res_ptr;
    MYSQL_ROW sqlrow;
    int res;

    // 初始化 MySQL 连接对象
    mysql_init(&my_connection);

    // 连接数据库
    if (!mysql_real_connect(&my_connection, DB_HOST, DB_USER, DB_PASS, DB_NAME, 0, NULL, 0)) {
        handle_error(&my_connection, "Connection failed");
    }

    printf("Connection success
");

    // 插入数据到表中
    res = mysql_query(&my_connection, "INSERT INTO children(fname, age) VALUES('Robert', 7)");
    if (res) {
        handle_error(&my_connection, "Insert error");
    } else {
        printf("Inserted %lu rows
", (unsigned long)mysql_affected_rows(&my_connection));
    }

    // 查询最后插入的数据
    res = mysql_query(&my_connection, "SELECT LAST_INSERT_ID()");
    if (res) {
        handle_error(&my_connection, "SELECT error");
    }

    // 获取查询结果
    res_ptr = mysql_use_result(&my_connection);
    if (res_ptr) {
        // 迭代结果集并输出
        while ((sqlrow = mysql_fetch_row(res_ptr))) {
            printf("We inserted childno %s
", sqlrow[0]);
        }
        // 释放结果集
        mysql_free_result(res_ptr);
    } else {
        fprintf(stderr, "Failed to retrieve result set.
");
    }

    // 关闭连接
    mysql_close(&my_connection);

    return EXIT_SUCCESS;
}

可以看到成功插入了一行,并且通过LAST_INSERT_ID拿到了ID

这里附上书上数据对应的插入代码

-- Create the table children

CREATE TABLE children (
    childno INT(11) AUTO_INCREMENT NOT NULL PRIMARY KEY,
    fname VARCHAR(30),
    age INT
);

-- Populate the table 'children'

INSERT INTO children (fname, age) VALUES ('Jenny', 21);
INSERT INTO children (fname, age) VALUES ('Andrew', 17);
INSERT INTO children (fname, age) VALUES ('Gavin', 8);
INSERT INTO children (fname, age) VALUES ('Duncan', 6);
INSERT INTO children (fname, age) VALUES ('Emma', 4);
INSERT INTO children (fname, age) VALUES ('Alex', 15);
INSERT INTO children (fname, age) VALUES ('Adrian', 9);
INSERT INTO children (fname, age) VALUES ('Ann', 3);
INSERT INTO children (fname, age) VALUES ('Ann', 4);
INSERT INTO children (fname, age) VALUES ('Ann', 3);
INSERT INTO children (fname, age) VALUES ('Ann', 4);

除了上述两个基本的函数之外还有一些其他的函数,函数原型如下

MYSQL_RES *mysql_store_result(MYSQL *connection);
//从返回数据的语句中提取所有的数据,返回一个指向结果集的指针
my_ulonglong mysql_num_rows(MYSQL_RES *result);
//统计返回结果中的行数
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
//从结果集中提取一行,把这一行放在行机构里,每次使用一个
void mysql_data_seek(MYSQL_RES *result, my_ulonglong offset);
//在结果集中跳转,第二个参数是行号,设置下一个被fetch操作返回的行
MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES *result);
//返回一个偏移值,表示结果集中的当前位置(不是行号)
void mysql_free_result(MYSQL_RES *result);
//对结果集的操作完成后,需要调用这个函数清理已经分配的对象
MYSQL_RES *mysql_use_result(MYSQL *connection);
//提取一行数据,返回指向对象的指针,不会缓存
//适合处理大数据量的查询结果,因为它允许逐行读取而不会将所有行一次性加载到内存中,这样可以有效减少内存的使用
unsigned int mysql_field_count(MYSQL *connection);
//获取最近一次查询的结果集中字段(列)的数量
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result);
//用于从查询结果的字段列表中获取每个字段的描述信息,类似于字段的元数据。
//返回一个指向 MYSQL_FIELD 结构体的指针,这个结构体包含了字段的详细信息(例如字段名、类型等)。

下面是书上给出的一个综合程序(用chatgpt优化了),基本上用到了上面的所有函数,还有运行结果和具体的程序解释

#include <stdlib.h>
#include <stdio.h>
#include <mysql.h>

// MySQL 连接对象和结果集
MYSQL my_connection;
MYSQL_RES *res_ptr;
MYSQL_ROW sqlrow;

// 函数声明
void display_header();
void display_row();

int main(int argc, char *argv[]) {
    int res;
    int first_row = 1;  // 用于确保我们只显示一次表头

    // 初始化 MySQL 连接对象
    mysql_init(&my_connection);

    // 尝试连接到数据库
    if (mysql_real_connect(&my_connection, "localhost", "rick", "111111", "foo", 0, NULL, 0)) {
        printf("Connection success
");

        // 执行 SQL 查询,选择年龄大于 5 的记录
        res = mysql_query(&my_connection, "SELECT childno, fname, age FROM children WHERE age > 5");
        if (res) {
            // 查询执行失败,打印错误信息
            fprintf(stderr, "SELECT error: %s
", mysql_error(&my_connection));
        } else {
            // 使用 mysql_store_result() 获取结果集到内存
            res_ptr = mysql_store_result(&my_connection);
            if (res_ptr) {
                // 获取每行的数据并显示
                while ((sqlrow = mysql_fetch_row(res_ptr))) {
                    if (first_row) {
                        // 首次显示表头
                        display_header();
                        first_row = 0;
                    }
                    // 显示每行数据
                    display_row();
                }

                // 释放结果集资源
                mysql_free_result(res_ptr);
            } else {
                // 获取结果集失败,打印错误信息
                fprintf(stderr, "Failed to retrieve result set: %s
", mysql_error(&my_connection));
            }
        }

        // 关闭数据库连接
        mysql_close(&my_connection);
    } else {
        // 连接失败,打印错误信息
        fprintf(stderr, "Connection failed
");
        if (mysql_errno(&my_connection)) {
            fprintf(stderr, "Connection error %d: %s
",
                    mysql_errno(&my_connection), mysql_error(&my_connection));
        }
    }

    return EXIT_SUCCESS;
}

// 显示表头,列出字段名称及其相关属性
void display_header() {
    MYSQL_FIELD *field_ptr;//获得列名

    printf("Column details:
");
    while ((field_ptr = mysql_fetch_field(res_ptr)) != NULL) {
        // 显示字段的名称
        printf("	 Name: %s
", field_ptr->name);
        printf("	 Type: ");

        // 判断字段类型并显示
        if (IS_NUM(field_ptr->type)) {//数字
            printf("Numeric field
");
        } else {
            switch (field_ptr->type) {//字符串
                case FIELD_TYPE_VAR_STRING:
                    printf("VARCHAR
");
                    break;
                case FIELD_TYPE_LONG:
                    printf("LONG
");
                    break;
                default:
                    printf("Type is %d, check in mysql_com.h
", field_ptr->type);
            }
        }

        // 显示字段的最大长度
        printf("	 Max width %ld
", field_ptr->length);

        // 检查字段是否具有 AUTO_INCREMENT 属性
        if (field_ptr->flags & AUTO_INCREMENT_FLAG) {
            printf("	 Auto increments
");
        }
        printf("
");
    }
}

// 显示查询结果的每一行
void display_row() {
    unsigned int field_count;

    // 遍历每一列,打印其值
    field_count = 0;
    while (field_count < mysql_num_fields(res_ptr)) {//如果域名数量比当前索引大
        if (sqlrow[field_count]) {//sqlrow存了这一行的数据
            printf("%s ", sqlrow[field_count]);
        } else {
            printf("NULL ");
        }
        field_count++;
    }
    printf("
");
}

还有更多的函数,在此不再赘述,可以查看MYSQL手册相关

总结

可以看到C语言已经有了很成熟的与MySQL交互的接口,并且大部分函数的使用是和MySQL版本不相关的,但实际在Ubuntu运行的时候还是需要注意MySQL的版本和包的问题,因为新版本的MySQL库修改了一些配置文件的位置和相关的依赖包

参考文献

  1. ubuntu 23.10.1 mysql 安装
  2. ChatGPT
相关推荐
一颗小树x22 分钟前
Llama 3.1 本地电脑部署 Linux系统 【轻松简易】
linux·llama·本地部署·3.1
Struggle Sheep1 小时前
linux安装redis
linux·运维·redis
计算机毕设指导61 小时前
基于SpringBoot的城乡商城协作系统【附源码】
java·spring boot·后端·mysql·spring·tomcat·maven
Smile丶凉轩2 小时前
数据库面试知识点总结
数据库·c++·mysql
butteringing2 小时前
BuildFarm Worker 简要分析
linux·软件构建·bazel·re api
RainbowSea3 小时前
9-1. MySQL 性能分析工具的使用——last\_query\_cost,慢查询日志
数据库·sql·mysql
猿java3 小时前
很多程序员会忽略的问题:创建 MySQL索引,需要注意什么?
java·后端·mysql
reset20214 小时前
ubuntu离线安装ollama
linux·ubuntu·ollama
放氮气的蜗牛4 小时前
Linux命令终极指南:从入门到精通掌握150+核心指令
linux·运维·服务器
DC_BLOG4 小时前
Linux-Ansible模块进阶
linux·运维·服务器·ansible