SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

SQLiteで組み込みDB体験

手軽にデータベース機能を実装できるC言語ライブラリ「SQLite」の利用

サンプルプログラムの実行

 「shell.c」は、SQLiteを操作するユーティリティとして便利ではありますが、結構きっちり作られていて、ソースを読むには、ちょっと長いかも知れません。そこで、もっと短いサンプルプログラムを作りましたので、そちらを使ってライブラリの使い方を説明します。

sample.c
// sample.c
// written by mnagaku @ 2006/04/27
// id, worker, change_line_count, commit_date
// の形式のデータをDBに取り込み、
// change_line_countの総計の多いworkerのtop10と
// commit数(データ数)の総計の多いworkerのtop10を調べます

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "sqlite3.h"

#define MAX_STR 256
#define MAX_COLUMN 4
// SQL文
#define CREATE_TABLE "CREATE TABLE sample (id INTEGER PRIMARY KEY, \
    worker TEXT NOT NULL, change_line_count INTEGER NOT NULL, \
    commit_date TEXT NOT NULL)"
#define INSERT_TABLE "INSERT INTO sample (id, worker, change_line_count, \
    commit_date) values (%s, '%s', %s, '%s')"
#define SELECT_TABLE "SELECT worker, sum(change_line_count) \
    AS total_change_line_count FROM sample GROUP BY worker \
    ORDER BY total_change_line_count DESC LIMIT 10"
#define SELECT_TABLE2 "SELECT worker, count(id) AS total_commit_count \
    FROM sample GROUP BY worker ORDER BY total_commit_count DESC LIMIT 10"

// DBの検索結果を大域変数で保持
typedef struct {
    char values[MAX_COLUMN][MAX_STR];
} RAW;
RAW *sqlResult;
int rawSize, columnSize;

// 検索結果を1行取得する度に呼ばれるコールバック関数
static int callback(void *option, int columnCount, char **columnValues,
    char **columnNames) {
    int columnCounter;

    if(sqlResult == NULL) {
        sqlResult = (RAW *)malloc(MAX_COLUMN * MAX_STR * 2);
        columnSize = columnCount;
    } else if((sqlResult = (RAW *)realloc(sqlResult,
        MAX_COLUMN * MAX_STR * (rawSize + 2))) == NULL)
        return -1;

    for(columnCounter = 0;
        columnCounter < MAX_COLUMN && columnCounter < columnCount;
        columnCounter++) {
        if(rawSize == 1)
            strncpy(sqlResult[0].values[columnCounter],
                columnNames[columnCounter], MAX_STR);
        strncpy(sqlResult[rawSize + 1].values[columnCounter],
            columnValues[columnCounter], MAX_STR);
    }
    rawSize++;
    return 0;
}

// SQLの実行
void sqlExec(sqlite3 *db, const char *sql, sqlite3_callback cb) {
    char *pStr;

    free(sqlResult);
    sqlResult = NULL;
    rawSize = 0;

    if(sqlite3_exec(db, sql, cb, NULL, &pStr) != SQLITE_OK) {
        fprintf(stderr, "Error %s %d: %s\n%s\n", __FILE__, __LINE__, pStr, sql);
        sqlite3_close(db);
        exit(0);
    }
}

// メイン
void main() {
    char str[MAX_STR], data[4][MAX_STR], *pStr;
    int lineCount, dataCount;
    FILE *fp;
    sqlite3 *db;

// メモリ上にDBを構築
    if(sqlite3_open(":memory:", &db)) {
        fprintf(stderr, "Error %s %d: Can't open database %s\n",
            __FILE__, __LINE__, sqlite3_errmsg(db));
        sqlite3_close(db);
        return;
    }
// テーブル作成
    sqlExec(db, CREATE_TABLE, NULL);
// データファイル読込み
    if((fp = fopen("sample.csv", "rt")) == NULL) {
        fprintf(stderr, "Error %s %d: Can't open datafile.\n",
            __FILE__, __LINE__);
        sqlite3_close(db);
        return;
    }
    for(lineCount = 0; fgets(str, MAX_STR, fp) != NULL; lineCount++) {
        for(pStr = strtok(str, ","), dataCount = 0;
            dataCount < 4 && strlen(pStr) > 0 && pStr[0] != '\n';
            pStr = strtok(NULL, ","), dataCount++)
            strncpy(data[dataCount], pStr, MAX_STR);
        if(dataCount < 4) {
            printf("Warnning %s %d: Find illegal data at %d\n",
                __FILE__, __LINE__, lineCount);
            break;
        }
// 読み込んだデータをDBに投入
        sprintf(str, INSERT_TABLE, data[0], data[1], data[2], data[3]);
        sqlExec(db, str, NULL);
    }
    fclose(fp);
// DBの検索
    puts("total_change_line_count top10");
    sqlExec(db, SELECT_TABLE, callback);
    for(lineCount = 0; lineCount <= rawSize; lineCount++) {
        for(dataCount = 0; dataCount < columnSize; dataCount++)
            printf("%s,", sqlResult[lineCount].values[dataCount]);
        puts("");
    }
    puts("");
    puts("total_commit_count top10");
    sqlExec(db, SELECT_TABLE2, callback);
    for(lineCount = 0; lineCount <= rawSize; lineCount++) {
        for(dataCount = 0; dataCount < columnSize; dataCount++)
            printf("%s,", sqlResult[lineCount].values[dataCount]);
        puts("");
    }
    sqlite3_close(db);
}

 「sample.c」が、そのサンプルプログラムですが、「sample.c」で処理を行うためのデータが必要になりますので、サンプルデータを生成する「makedata.c」も作成しました。

makedata.c
// makedata.c
// written by mnagaku @ 2006/04/27
// ダミーデータを生成します
// データ形式は次のとおり
// id, worker, change_line_count, commit_date

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

#define MAX_LINE 100000

void main() {
    int i;
    int year = 2000;
    int month = 1;
    int day = 1;
    int hour = 0;
    int minutes = 0;
    int second = 0;

    srand(time(NULL));

    for(i = 0; i < MAX_LINE; i++) {
        second = (second + rand()) % 60;
        minutes += rand() % 60 + 1;
        if(minutes >= 60) {
            minutes %= 60;
            hour++;
        }
        if(hour >= 24) {
            hour %= 24;
            day++;
        }
        if(day >= 29) {
            day = 1;
            month++;
        }
        if(month >= 13) {
            month = 1;
            year++;
        }
        printf("%d,%c%c,%d,%04d-%02d-%02d %02d:%02d:%02d,\n",
            i + 1, (rand() % 2) ? ('a' + rand() % 26) : ('A' + rand() % 26),
            (rand() % 2) ? ('a' + rand() % 26) : ('A' + rand() % 26),
            rand() % 100 + 1, year, month, day, hour, minutes, second);
    }
}

 まず、「makedata.c」をコンパイル・実行し、サンプルデータを作成してください。

サンプルデータを用意する
> cl makedata.c
> makedata > sample.csv

 作成されたサンプルデータは、オープンソースプロジェクトのソースリポジトリに対するコミットの履歴だと考えてください。データは、通し番号を示すid、コミットした作業者名を示すworker、ソースの変更行数を示すchange_line_count、コミットした日時を示すcommit_dateで構成され、コミット10万件分のデータとなっています。カンマ区切りテキストではありますが、10万件もあるので、Excelでは開けません。

 次に、「sample.c」をコンパイル・実行し、サンプルデータから、ソースの変更行数の総計が多い作業者top10と、コミットした回数が多い作業者top10を調べます。

サンプルプログラムを実行する
> cl /D_CRT_SECURE_NO_DEPRECATE sample.c sqlite335.lib
> sample
total_change_line_count top10
worker,total_change_line_count,
Kx,3138,
zJ,3035,
QQ,2979,
OD,2901,
ga,2848,
xJ,2824,
HH,2820,
qS,2810,
DJ,2803,
Uw,2799,

total_commit_count top10
worker,total_commit_count,
Kx,61,
Qm,56,
Jp,55,
HW,54,
HJ,53,
HN,53,
Kt,53,
uw,53,
xR,53,
AT,52,

 「sample.c」はSQLiteの機能を使うので、「shell.c」同様に、ライブラリとリンクさせます。

 実行すると数分で結果が表示されるでしょう。最近のそこそこのスペックのPCであれば、1分程度で結果が出るでしょう。

次のページ
サンプルプログラムをのぞいてみる

この記事は参考になりましたか?

特集記事連載記事一覧

もっと読む

この記事の著者

長久 勝(ナガク マサル)

1972年1月10日生まれ。京都府出身。1994年龍谷大学理工学部数理情報学科卒業。ゲーム会社に入社。「ワンダースワンゲームプログラミング」「Javaゲームプログラミング」等を執筆。早稲田大学MNC非常勤講師。神楽坂酔っ払い研究所所長。ホームページはhttp://www6.plala.or.jp/mnagaku/。blog「mnagakuのx86_64地獄」も毎日更新中。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/374 2006/05/02 00:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー