linux程序设计——执行SQL语句(第八章)

58 篇文章 0 订阅
57 篇文章 9 订阅

8.3    使用C语言访问MySQL数据

4.处理返回的数据
MySQL返回两种类型的数据
1.从表中提取的信息,也就是列数据
2.关于数据的数据,即所谓的元数据(metadata),例如列名和类型.
mysql_field_count函数提供了一些关于查询结果的基本信息,它接受连接对象,并返回结果集中的字段(列)数目:
unsigned int mysql_field_count(MYSQL *connection);
在更通用的情况下,可以用mysql_field_count做其他事情,比如判断为何mysql_store_result的调用会失败.例如,如果mysql_store_result返回,但是mysql_field_count返回一个正数,可以推测出这个是一个提取错误.但是如果mysql_field_count返回0,则表示没有列可以提取,这可以解释为何存储结果会失败.对于通用查询处理模块或任何随意构造查询的情况,这个函数都非常有用.
编写程序select3.c输出数据
输出的数据不是很整洁,如果需要格式化的数据,则要同时得到MySQL返回的数据和元数据.使用mysql_fetch_field来同时将元数据和数据提取到一个新的结构中:
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result);
需要重复调用此函数,直到返回表示数据结束的NULL值为止.然后,可以使用指向字段结构的数据的指针来得到关于列的信息.结构MYSQL_FIELD定义在mysql.h中.
MYSQL_FIELD中的成员                说明
char *name;                        列名,为字符串
char *table;                    列所属的表名.当一个查询要使用多个表时,特别有用.
char *def;                        如果调用mysql_list_fields,它将包含该列的默认值
enum enum_field_types type;        列类型
unsigned int length;            列宽
unsigned int max_length;        如果使用mysql_store_result,它将包含以字节为单位的提取的最长列值的长度
unsigned int flags;                列定义的标志    
unsigned int decimals;            小数点后的数字个数.
此外,使用下面的函数覆盖当前的字段编号:
MYSQL_FIELD_OFFSET mysql_field_seek(MYSQL_RES *result, MYSQL_FIELD_OFFSET offset);
该编号会随每次mysql_fetch_field调用而自动增加.如果给参数offset传递值0,将跳回第一列.
编写程序select4.c
8.3.4更多的函数
示例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);            返回最近执行的查询信息
int mysql_select_db(MYSQL *connection, const char *dbname);
如果用户拥有权限,则把默认数据库改为参数指定的数据库
int mysql_shutdown(MYSQL *connection, enum mysql_enum_shutdown_level);
如果用户拥有权限,则关闭连接的数据库服务器。

8.4    CD数据库应用程序

创建一个新的数据库,然后将其作为当前的数据库:
create database blpcd;
use blpcd;
只考虑数据的两种最重要的关系
首先,每张CD由不同数目的曲目组成,所以将把曲目数据存在一个独立于其他CD数据的表中。
其次,每位艺术家经常有多张专辑,所以将艺术家的信息存储一次,然后单独提取属于该艺术家的所有CD。
8.4.1 创建表
需要去确定表的实际结构。从主表——CD表开始,它保存大部分的信息。需要保存一个CD ID、一个分类号、一个标题以及一些标注。还需要一个来自artist表的ID号来表明是哪位艺术家制作了这张专辑。
artist表很简单,它仅仅保存艺术家的名字和一个唯一的艺术家ID号。track表也很简单,只需要一个CD ID来表明曲目属于哪张CD、一个曲目号和一个曲目标题。
首先是CD表:
CREATE TABLE cd(
        id int auto_increment NOT NULL primary key,
        title varchar(70) NOT NULL,
        artist_id int NOT NULL,
        catalogue varchar(30) NOT NULL,
        notes varchar(100)
);
创建表cd,它包含下面一些列:
id列,包含一个自动增加的整数,它是表的主键
最长为70个字符的title
artist_id,在artist表中使用的一个整数
最长为30个字符的catalogue号
最长为100个字符的notes
只有notes列可以为NULL,其他列都必须包含值
其次是artist表:
CREATE TABLE artist(
        id int auto_increment NOT NULL primary key,
        name varchar(100) NOT NULL
);
最后是track表:
CREATE TABLE track(
        cd_id int NOT NULL,
        track_id int NOT NULL,
        title varchar(70),
        primary key(cd_id, track_id)
);
这次使用不同的方法来声明主键,track表的不同之处在于每张CD的ID会出现多次,而对于任何指定曲目的ID,例如曲目1,也会在不同CD中出现多次。但是,这两者的结合将永远是唯一的,所以将主键声明为这两列的结合,这被称为联合键。
将这些SQL语句存储在文件create_table.sql中,并将该文件保存在当前目录中,然后开始创建数据库以及其中的表。
8.4.2 添加数据
编写insert_data.sql文件,使用\.命令执行。
注意这个文件将删除数据库blpcd中所有的数据以确保脚本是干净的
此外,这个文件在ID字段中插入数值,而不是让auto_increment自动分配的。
使用mysql命令客户端和一些SQL语句来检查。首先,从数据库中选出每张专辑的头两首曲目:
SELECT artist.name, cd.title AS "CD Title", track.track_id, track.title AS "Track" FROM artist, cd, track WHERE artist.id = cd.artist_id AND track.cd_id = cd.id AND track.track_id < 3;
这条SQL语句的第一部分是:
SELECT artist.name, cd.title, track.track.id, track.title
它通过使用标记tablename.column来说明想要显示哪些列。
SELECT语句的AS部分SELECT artist.name, cd.title AS "CD Title", track.track_id和track.title AS "Track"只是在输出时重命名列名。因此,来自cd表的title列(cd.title)的标题栏被命名为"CD Title",track.title列被命名为"Track"。AS的使用给了更好的输出,它是在命令行中针对SQL语句的一个有用的字句,但是当通过其他编程语言来调用SQL语句时,几乎不会用到它。
FROM artist, cd, track
它告诉服务器使用的表名
WHERE子句是需要点技巧的部分:
WHERE artist.id = cd.artist_id AND track.cd_id = cd.id AND track.track_id < 3
第一部分告诉服务器artist表中的ID应与cd表中的artist_id相同。第二部分告诉服务器track表的cd_id列应该与cd表中的id列相同,第三部分track.track_id<3减少了返回数据的数量以使得仅仅从每张CD中得到曲目1和曲目2.最后,使用AND把3个条件结合起来,因为需要这3个条件同时都为真。
8.4.3 使用C语言访问数据
编写类似于CD应用程序的一个常见问题是无法知道返回的结果数,以及如何在客户端代码和访问数据库的代码间传递这些结果。在这个应用程序中,为了保持简单并专注于数据库接口,将使用固定大小的结构。但在实际的程序中,这可能是不能接受的。一种常见的解决办法(它同时也有助于减少网络流量)是每次总是提取一行数据,正如前面介绍的mysql_use_result和mysql_fetch_row一样。
1.接口定义
先从头文件app_mysql.h开始,它定义了结构和函数:
首先是一些结构:
/* A simplistic structure to represent the current CD, excluding the track information */
struct current_cd_st{
    int artist_id;
    int cd_id;
    char artist_name[100];
    char title[100];
    char catalogue[100];
};
/* A simplistic track details structure */
struct current_tracks_st{
    int cd_id;
    char track[20][100];
};
#define MAX_CD_RESULT 10
struct cd_search_st{
    int cd_id[MAX_CD_RESULT];
}
然后是一对函数,它们用于连接数据库以及从数据库断开连接
/* Database backend functions */
int database_start(char *name, char *password);
void database_end();
现在,转向操纵数据的函数,注意,没有创建或三处艺术家的函数,将在后台实现它,根据需要创建艺术家条目,然后当它们不在被任何专辑使用的时候将其删除。
/* Functions for adding a CD */
int add_cd(char *artist, char *title, char *catalogue, int *cd_id);
int add_tracks(struct current_tracks_st *track);
/* Functions for finding and retrieving a CD */
int find_cds(char *search_str, struct cd_search_st *results);
int get_cd(int cd_id, struct current_cd_st *dest);
int get_cd_tracks(int cd_id, struct current_tracks_st *dest);
/* Function for deleting items */
int delete_cd(int cd_id);
搜索函数相当通用:当传递一个字符串,然后它将在artist、title或catalogue条目中搜索该字符串。
2.测试应用程序接口
在实现接口之前,先编一些代码使用它,这看起来有点奇怪,但在开始实现接口之前了解一下它将如何运转通常是个好办法。
下面是app_test.c的源代码,首先是一些includes和structs:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "app_mysql.h"
int main(){
    struct current_cd_st cd;
    struct cd_search_st cd_res;
    struct current_tracks_st ct;
    int cd_id;
    int res, i;
    /* 应用程序的第一件事始终是初始化一个数据库连接并提供一个正确的用户名和密码 */
    database_start("rick", "secret");
    /* 测试添加一张CD */
    res = add_cd("Mahler", "Symphony No 1", "4596102", &cd_id);
    printf("Result of adding a cd was %d, cd_id is %d\n", res, cd_id);

    memset(&ct, 0, sizeof(ct));
    ct.cd_id = cd_id;
    strcpy(ct.track[0], "Langsam Schleppend");
    strcpy(ct.track[1], "Krafig bewag");
    strcpy(ct.track[2], "Feierlich und gemeseen");
    strcpy(ct.track[3], "Sturmish bewegt");
    add_tracks(&ct);
    /* 现在搜索CD,并从找到的第一张CD中提取信息 */
    res = find_cds("Symphony", &cd_res);
    printf("Found %d cds, first has ID %d\n", res, cd_res.cd_id[0]);

    res = get_cd(cd_res.cd_id[0], &cd);
    printf("get_cd returned %d\n", res);

    memset(&ct, 0, sizeof(ct));
    res = get_cd_tracks(cd_res.cd_id[0], &ct);
    printf("get_cd_tracks returned %d\n", res);
    printf("Title: %s\n", cd.title);
    i = 0;
    while(i < res){
        printf("\ttrack %d is %s\n", i, ct.track[i]);
        i++;
    }

    /* 最后删除CD */
    res = delete_cd(cd_res.cd_id[0]);
    printf("Delete_cd returned %s\n", res);
    /* 断开连接并退出 */
    database_end();
    return EXIT_SUCCESS;
}
3.实现接口
实现指定的接口,这些包含在文件app_mysql.c中
首先是一些基本的includes、需要的全局连接结构和一个标志dbconnected,使用它来确保程序不会在没有建立连接的情况下尝试访问数据。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值