zoukankan      html  css  js  c++  java
  • C基础 mariadb处理简单案例

    引言

       MariaDB 是一款灰常不错开源数据库. 这里直接用它来解决业务问题.

    业务需求:

      现在数据库中表示按照天分表的. 突然我们需要按照月来处理数据.

    例如输入一个玩家id, 查找这个玩家这个月内看了一件事几次. 我们先搭建一个环境.

    操作系统:

    Linux version 4.4.0-22-generic (buildd@lgw01-41)
    (gcc version 5.3.1 20160413 (Ubuntu 5.3.1-14ubuntu2) ) 
    #40-Ubuntu SMP Thu May 12 22:03:46 UTC 2016

     首先安装 MariaDB数据库

    sudo apt-get install mariadb-server
    sudo apt-get install mariadb-client
    sudo apt-get install libmariadb2
    sudo apt-get install libmariadb-client-lgpl-dev
    sudo apt-get install libreoffice-mysql-connector

    后面是C访问 MariaDB驱动. 这里扯一点, 目前关于MariaDB不懂问题, 搜不见直接当成mysql开始搜.

    MariaDB安装成功后默认是开启的, 看下面图描述

    后面搭建测试环境 首先 看 oss_musicelves.sql

    -- MySQL dump 10.10
    --
    -- Host: localhost    Database: oss_log
    -- ------------------------------------------------------
    -- Server version    5.5.24-tmysql-1.4
    
    /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
    /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
    /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
    /*!40101 SET NAMES utf8 */;
    /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
    /*!40103 SET TIME_ZONE='+00:00' */;
    /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
    /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
    /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
    /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
    
    --
    -- Table structure for table `oss_musicelves`
    --
    
    DROP TABLE IF EXISTS `oss_musicelves`;
    CREATE TABLE `oss_musicelves` (
      `record_id` bigint(20) NOT NULL AUTO_INCREMENT,
      `account_id` bigint(20) NOT NULL,
      `server_id` int(11) NOT NULL,
      `char_id` bigint(20) NOT NULL,
      `char_sex` int(11) NOT NULL,
      `type_id` int(11) NOT NULL,
      `timeStamp` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
      `ptype` int(11) NOT NULL,
      `specifytype` int(11) NOT NULL,
      `childtype` int(11) NOT NULL,
      PRIMARY KEY (`record_id`),
      KEY `idx_specifytype` (`specifytype`)
    ) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=latin1;
    
    --
    -- Dumping data for table `oss_musicelves`
    --
    
    
    /*!40000 ALTER TABLE `oss_musicelves` DISABLE KEYS */;
    LOCK TABLES `oss_musicelves` WRITE;
    INSERT INTO `oss_musicelves` VALUES (1,411948833,84869352,27899597414400801,0,1812,'2016-05-31 14:27:41',0,1,1),(2,1344702709,90964200,30422720614402293,0,1812,'2016-05-31 14:58:26',0,1,1),(3,706409913,90964200,30422720614401465,1,1812,'2016-05-31 14:58:27',0,1,2),(4,706409913,392964857,30422720614401465,1,1812,'2016-05-31 14:58:59',0,2,4),(5,1344702709,392964857,30422720614402293,0,1812,'2016-05-31 14:58:59',0,2,4),(6,706409913,90964200,30422720614401465,1,1812,'2016-05-31 15:04:52',0,1,2),(7,706409913,392964857,30422720614401465,1,1812,'2016-05-31 15:05:54',0,2,4),(8,1344702709,392964857,30422720614402293,0,1812,'2016-05-31 15:05:54',0,2,4),(9,1344702709,90964200,30422720614402293,0,1812,'2016-05-31 15:10:29',0,1,1),(10,706409913,90964200,30422720614401465,1,1812,'2016-05-31 15:10:32',0,1,2),(11,1344702709,392964857,30422720614402293,0,1812,'2016-05-31 15:10:54',0,2,4),(12,3145910262,90964200,29520779366416374,1,1812,'2016-05-31 15:30:00',0,1,1),(13,1372825842,90964200,30173879500803314,1,1812,'2016-05-31 15:30:01',0,1,2),(14,3145910262,392964857,29520779366416374,1,1812,'2016-05-31 15:30:04',0,2,4),(15,1372825842,392964857,30173879500803314,1,1812,'2016-05-31 15:30:04',0,2,4),(16,3145910262,392964857,29520779366416374,1,1812,'2016-05-31 15:34:24',0,2,4),(17,1372825842,392964857,30173879500803314,1,1812,'2016-05-31 15:34:24',0,2,4),(18,706409913,90964200,30422720614401465,1,1812,'2016-05-31 15:40:14',0,1,1),(19,1344702709,90964200,30422720614402293,0,1812,'2016-05-31 15:40:16',0,1,2),(20,3145910262,392964857,29520779366416374,1,1812,'2016-05-31 15:42:19',0,2,4),(21,1372825842,392964857,30173879500803314,1,1812,'2016-05-31 15:42:19',0,2,4),(22,1027763684,90964200,30175730790400484,0,1812,'2016-05-31 16:56:33',1,1,1),(23,1372825842,90964200,30173879500803314,1,1812,'2016-05-31 16:56:50',0,1,2),(24,1372825842,392964857,30173879500803314,1,1812,'2016-05-31 16:57:37',0,2,3),(25,1027763684,392964857,30175730790400484,0,1812,'2016-05-31 16:57:37',1,2,3),(26,1372825842,392964857,30173879500803314,1,1812,'2016-05-31 17:04:33',0,2,3),(27,1027763684,392964857,30175730790400484,0,1812,'2016-05-31 17:04:33',1,2,3),(28,1027763684,90964200,30175730790400484,0,1812,'2016-05-31 17:14:15',1,1,2),(29,1372825842,392964857,30173879500803314,1,1812,'2016-05-31 17:14:50',0,2,3),(30,1027763684,392964857,30175730790400484,0,1812,'2016-05-31 17:14:50',1,2,3),(31,751699770,90964200,30175199027201850,1,1812,'2016-05-31 18:14:59',1,1,1);
    UNLOCK TABLES;
    /*!40000 ALTER TABLE `oss_musicelves` ENABLE KEYS */;
    /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
    
    /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
    /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
    /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
    /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
    /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
    /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
    /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

    这个 oss_musicelves.sql 文件主要功能是创建 oss_musicelves数据库, 并填充数据.

    还有一个 搭建环境 的 脚本 mariadb_test.sql 和上一个sql文件放在同一个目录下.

    # 创建一个测试数据库
    create database oss_log;
    
    # 进入oss_log 数据库
    use oss_log;
    
    # 创建 oss_musicelves 数据库, 并导入数据
    source oss_musicelves.sql;
    
    # 批量创建表和数据
    create table 2016_6_1_oss_musicelves select * from oss_musicelves;
    create table 2016_6_2_oss_musicelves select * from oss_musicelves;
    create table 2016_6_3_oss_musicelves select * from oss_musicelves;
    create table 2016_6_4_oss_musicelves select * from oss_musicelves;
    create table 2016_6_5_oss_musicelves select * from oss_musicelves;
    create table 2016_6_9_oss_musicelves select * from oss_musicelves;
    create table 2016_6_10_oss_musicelves select * from oss_musicelves;
    create table 2016_6_12_oss_musicelves select * from oss_musicelves;
    
    # 查询表是否创建成功
    show tables;
    
    # 这里处理 拿到的数据
    select distinct table_name from information_schema.columns where table_name like '2016_6_%_oss_musicelves';

    直接放在 MariaDB控制台中直接刷进去. 搭建的具体环境如下

    到这里环境基本搭建好了. MariaDB入门等等, 完全可以当做mysql 学习温故一遍.

    前言

      上面问题就是 原本 是 select * from oss_musicelves; 就可以解决的问题.

    这里 需要 输入年和月 外加一些特殊条件 . select * from %_%_%_oss_musicelves; 解决. 单纯用sql脚本也可以解决.非常复杂.用的不熟.

    这里首先通过 shell 脚本处理

    touch getmouths.sh
    chmod +x getmouths.sh
    vi getmouths.sh

     具体的脚本 内容 如下

    #!/bin/sh
    
    #得到输入的玩家ptid
    if [ $# -lt 1 ]
    then
        echo "uage: $0 [ptid]"
        exit -1
    fi
    ptid=$1
    
    mouth=$(date +%m | sed s'/^0//')
    #第一个参数是月份
    if [ $# -ge 2 ]
    then
        mouth=$2
    fi
    
    #第二个参数是年
    year=$(date +%Y)
    if [ $# -ge 3 ]
    then
        year=$3
    fi
    
    #得到查询的随机表名
    tbname=""${year}_${mouth}_%_oss_musicelves""
    
    #这里得到mysql 中所有合法表名
    rm -rf __tmp
    touch __tmp
    
    #开始查询数据库了, 需要以root权限启动这个脚本
    mysql -e "select distinct table_name from information_schema.columns where table_name like $tbname" | awk 'NR>1' | while read name
    do
        mysql -e "select count(*) from oss_log.$name where specifytype = 1 and char_id = $ptid" | awk 'NR>1' | while read cut
        do
            echo "$name : $cut"
            echo $cut >> __tmp
            break
        done
    done
    
    #统计表里面的数据
    sum=$(cat __tmp | awk '{s+=$1} END {print s}')
    rm -rf __tmp
    
    # 最后输出统计结果
    echo "$year-$mouth sum: $sum"

    使用脚本  截图

    通过shell可以完成 我们的需求. Linux上shell真好用. window的bat不好用.

    正文

    第一部分 : 让C调用MariaDB跑通

    先看 测试Demo mariadb_demo.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <mysql.h>
    
    /*
     * 第一个 mariadb程序
     */
    int main(int argc, char *argv[]) {
        // 创建数据连接对象
        MYSQL *con = mysql_init(NULL);
          if (con == NULL) {
            fprintf(stderr, "%s
    ", mysql_error(con));
            exit(EXIT_FAILURE);
          }
    
          if (!mysql_real_connect(con, "localhost", "root", "", NULL, 0, NULL, 0)) {
            fprintf(stderr, "%s
    ", mysql_error(con));
             mysql_close(con);
             exit(EXIT_FAILURE);
          }  
    
          if (mysql_query(con, "show databases;")) {
            fprintf(stderr, "%s
    ", mysql_error(con));
            mysql_close(con);
            exit(EXIT_FAILURE);
          }
    
        puts("mariadb is connect and run succesed!");
        mysql_close(con);    
    
        return 0;
    }

     具体的编译 命令

    su root
    gcc -Wall -ggdb2 -I/usr/include/mariadb -o mariadb_demo.out mariadb_demo.c -lmysqlclient
    ./mariadb_demo.out 

    运行结果 如下 

    到这里基本C 调用 MariaDB 基本流程跑通了. 但是很不爽. 只能通过root用户使用.

    那我们改变这里不爽. 进入第二部分. 扩展资料  c in mariadb  http://stackoverflow.com/questions/17265471/using-mariadb-in-c

    第二部分 : 通过普通用户完成业务需求.

    先创建普通用户 csz, 密码是 1413222, 并并且给其 select 读权限.

    su root
    mysql
    
    drop user csz;
    create user 'csz'@'%' identified by '13142222';
    grant select on *.* to 'csz'@'%';
    # 立即刷新
    flush privileges;

     后面登录试试

    mysql -ucsz -p1314222 -h127.0.0.1

     

     主要是mariadb默认关闭远程访问. 后面我们开启安全访问模式试试

    su root
    /etc/init.d/mysql stop
    mysqld_safe --skip-grant-tables

    后面再开启一个会话 . 重新输入 mysql -ucsz -p1314222 -h127.0.0.1 , 解决可以了
     

    到这里 通过新建的 csz 用户访问成功. 好了. 那我们开始写一个 msyql c 代码处理我们的业务. 按照月来访问. 那我们开始写代码.代码的总的思路是拼接一个这样的串

    #目标拼接 串内容
    select sum(c) from (
    select count(*) as c from 2016_6_1_oss_musicelves where specifytype=1 and char_id = 30422720614402293
    union all
    select count(*) from 2016_6_2_oss_musicelves where specifytype=1 and char_id = 30422720614402293
    union all
    select count(*) from 2016_6_3_oss_musicelves where specifytype=1 and char_id = 30422720614402293
    union all
    select count(*) from 2016_6_4_oss_musicelves where specifytype=1 and char_id = 30422720614402293
    union all
    select count(*) from 2016_6_5_oss_musicelves where specifytype=1 and char_id = 30422720614402293
    union all
    select count(*) from 2016_6_9_oss_musicelves where specifytype=1 and char_id = 30422720614402293
    union all
    select count(*) from 2016_6_10_oss_musicelves where specifytype=1 and char_id = 30422720614402293
    union all
    select count(*) from 2016_6_12_oss_musicelves where specifytype=1 and char_id = 30422720614402293
    ) as t;

    具体看 getmouths.c 文件 内容 

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <mysql.h>
    
    #define _INT_BUF (4098)
    // 得到查询数据表内容
    #define _STR_SQLTABLES 
        "select distinct table_name from information_schema.columns where table_name like '%d_%d_%%_oss_musicelves'"
    #define _STR_SQLSELECT 
        "select count(*) as c from %s where specifytype=1 and char_id = %lld"
    
    // 基础的mariadb 错误关闭函数
    static inline void _err_mariadb(MYSQL *con) {
        fprintf(stderr, "_err_mariadb error: %s
    ", mysql_error(con));
        mysql_close(con);
        exit(EXIT_FAILURE);
    }
    
    /*
     * 处理 oss_musicelves 一个月的所有表.
     */
    int main(int argc, char* argv[]) {
        long long ptid;
        time_t rt = time(NULL);
        struct tm *pt = localtime(&rt);
        int year = pt->tm_year + 1900;
        int mouth = pt->tm_mon + 1;    
    
        // 先简单检测输入
        if(argc <= 1) {
            fprintf(stderr, "%s [ptid] [mouth] [year]
    ", argv[0]);
            exit(EXIT_FAILURE);
        }    
    
        // 先得到 ptid 数据
        ptid = atoll(argv[1]);    
        // 得到当前月份
        if(argc >= 3)
            mouth = atoi(argv[2]);
        // 得到当前年份
        if(argc >= 4)
            year = atoi(argv[3]);
        // 简单检测结果是否合法
        if(ptid < 0 || mouth <=0 || mouth>12 || year<1900) {
            fprintf(stderr, "%s %lld %d %d is error!
    ", argv[0], ptid, mouth, year);
            exit(EXIT_FAILURE);
        } 
    
        // 输出结果
        printf("%s %lld %d %d start run!
    ", argv[0], ptid, mouth, year);
    
        // 开始用mysql 访问我们需要访问的数据结果了
        MYSQL *con = mysql_init(NULL);
        if(con == NULL) {
          fprintf(stderr, "mysql_init error: %s
    ", mysql_error(con));
          exit(EXIT_FAILURE);
         }
          if(!mysql_real_connect(con, "127.0.0.1", "csz", "1314222", "oss_log", 0, NULL, 0))
            _err_mariadb(con);
        
        char sqls[_INT_BUF];
        int sqlen = 0;
        sprintf(sqls, _STR_SQLTABLES, year, mouth);
        if(mysql_query(con, sqls))
            _err_mariadb(con);
        
        // 开始得到结果
        MYSQL_RES *ret = mysql_store_result(con);
        if(NULL == ret)
            _err_mariadb(con);
        MYSQL_ROW row;
        int i = 0, nr = 0;
        while(!!(row = mysql_fetch_row(ret))) {    
            if(i == 0) {
                sqlen = sprintf(sqls, "select sum(c) from (
    " _STR_SQLSELECT, row[0], ptid);
                i = 1;
                continue;
            }        
            // 后面正常拼接
            nr = sprintf(sqls + sqlen, "
    union all
    " _STR_SQLSELECT, row[0], ptid);
            if((sqlen += nr) >= _INT_BUF) {
                fprintf(stderr, "sprintf while %d too length.
    ", sqlen);
                goto __return_free;
            }
        }
        if(i == 0)     {
            printf("sum %lld %d/%d: 0
    ", ptid, year, mouth);
            goto __return_free;
        }
        
        // 这里处理有的数据
        nr = sprintf(sqls + sqlen, "
    ) as t;");    
        if((sqlen += nr) >= _INT_BUF) {
            fprintf(stderr, "sprintf end %d too length.
    ", sqlen);
            goto __return_free;
        }
        // 内存用完了就直接释放
        mysql_free_result(ret);
        ret = NULL;
        
    
        printf("sql : 
    	%s
    ", sqls);
        // 开始输出统计结果
        if(mysql_query(con, sqls))
            _err_mariadb(con);
        if((ret = mysql_store_result(con))==NULL)
            _err_mariadb(con);
        //得到结果直接返回
        if(!!(row=mysql_fetch_row(ret)))
            printf("sum %lld %d/%d: %s
    ", ptid, year, mouth, row[0]);
        else
            puts("select is empty!");
    
            
    __return_free:
        // 释放用过的内存
        mysql_free_result(ret);
        // 关闭打开的 数据库访问对象
          mysql_close(con);
        return 0;
    }

     编译命令

    gcc -Wall -ggdb2 -I/usr/include/mariadb -o getmouths.out getmouths.c -lmysqlclient

    最终运行结果是

    如果想详细了解关于mariadb c驱动的api使用, 可以参照老外写的很好理解.

     mysqlc demo http://zetcode.com/db/mysqlc/

    到这里就结束了, 关于C 访问数据库能力也基本打通了.

    后记

      错误是难免, 欢迎学习进步~~~   未来什么都不确定, 可以确定是没有未来, 只有现在还在装逼 . 

    后面经过实践 找到更好的解决方案 采用 mysql 存储过程

    -- 最终结果令人满意, mysql存储过程
    delimiter $$
    drop procedure if exists oss_log.proc_oss_monopolycollect;
    -- yr:年份, mh:月份, qq:查询QQ号, cut:输出当月统计的个数
    create procedure oss_log.proc_oss_monopolycollect(in yr int, in mh int, in qq int, out cut int)
    begin
        set @ltname = concat(yr, '_', mh , '_%_oss_monopolycollect');
        
        select replace(
                group_concat(distinct(table_name)),
                ',',
                ' where specifytype = 1 and account_id = {account_id} union all select count(*) from oss_log.'
                ) into @sqlstr
        from information_schema.columns where table_name like @ltname;
        
        -- 添加前面和尾巴部分
        set @sqlstr = concat('select sum(c) into @sumc from (select count(*) as c from oss_log.', 
                             @sqlstr,
                             ' where specifytype = 1 and account_id = {account_id}) as t;');
        -- 替换占位符, 得到最终的串内容
        set @sqlstr = replace(@sqlstr, "{account_id}", qq);
        
        -- 预编译并执行
        prepare stmt from @sqlstr;
        -- 开始执行
        execute stmt;
        -- 删除预编译的结果
        deallocate prepare stmt;
        
        -- 返回结果
        set cut = @sumc;
    end
    $$
    delimiter ;

    测试 脚本 如下

    -- 测试上面存储过程
    call oss_log.proc_oss_monopolycollect(2016, 5, 9331453, @cut);
    select @cut;
    set @cut:=NULL;
    drop procedure if exists oss_log.proc_oss_monopolycollect

     这里基本就解决 遇到的问题. 最后想说一句, 真jb长!

  • 相关阅读:
    用于 Windows8 的 Wijmo Charts 图表控件
    Silverlight 版 C1OutlookBar 初体验
    TX Text Control文字处理教程(12)MS Word中字段的导入导出操作
    TX Text Control文字处理教程(5)数据库操作
    TX Text Control文字处理教程(10)创建报价单
    TX Text Control文字处理教程(9)XML文件
    在 ActiveReports 中嵌入 Spread 控件
    TX Text Control文字处理教程(8)使用超链接
    TX Text Control文字处理教程(11)使用文本框
    2012春节快到了我们聚会吧!
  • 原文地址:https://www.cnblogs.com/life2refuel/p/5574544.html
Copyright © 2011-2022 走看看