業務資料庫採「按天分表」:2016_6_1_oss_musicelves、2016_6_2_oss_musicelves … 現需「按月統計」某玩家特定事件次數。純 SQL 寫法極其複雜,改用 C 程式動態拼接 UNION ALL 查詢,再彙總。
# 安裝 MariaDB 伺服器與開發套件
sudo apt-get install mariadb-server mariadb-client \
libmariadb2 libmariadb-client-lgpl-dev
# 建立測試資料庫與分表
mysql -u root -p < mariadb_test.sql
mariadb_test.sql 內容:建立 oss_log 資料庫、匯入基礎表 oss_musicelves、以 CREATE TABLE ... SELECT 批量建立 8 張分表。
#!/bin/sh
# getmouths.sh ptid [month] [year]
ptid=$1
mouth=${2:-$(date +%m | sed 's/^0//')}
year=${3:-$(date +%Y)}
tbname="${year}_${mouth}_%_oss_musicelves"
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' >> __tmp
done
sum=$(awk '{s+=$1} END {print s}' __tmp)
echo "$year-$mouth sum: $sum"
getmouths.cptid、月份、年份(預設為當月)。csz(僅 SELECT 權限),啟用 --skip-grant-tables 繞過預設只允許本地 root 登入。information_schema.columns 取得符合 YYYY_MM_%_oss_musicelves 模式的所有表名。SELECT SUM(c) FROM ( SELECT COUNT(*) AS c FROM t1 WHERE ... UNION ALL SELECT COUNT(*) FROM t2 WHERE ... ... ) AS t;
#define _INT_BUF 4096
#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"
MYSQL *con = mysql_init(NULL);
mysql_real_connect(con, "127.0.0.1", "csz", "1314222", "oss_log", 0, NULL, 0);
/* 1. 取得分表清單 */
sprintf(sql, _STR_SQLTABLES, year, month);
mysql_query(con, sql);
MYSQL_RES *res = mysql_store_result(con);
/* 2. 動態拼接 UNION ALL */
int first = 1, len = 0;
MYSQL_ROW row;
while ((row = mysql_fetch_row(res))) {
if (first) {
len = sprintf(sql, "SELECT SUM(c) FROM (\n" _STR_SQLSELECT, row[0], ptid);
first = 0;
} else {
len += sprintf(sql + len, "\nUNION ALL\n" _STR_SQLSELECT, row[0], ptid);
}
}
sprintf(sql + len, "\n) AS t;");
/* 3. 執行彙總查詢 */
mysql_free_result(res);
mysql_query(con, sql);
res = mysql_store_result(con);
if ((row = mysql_fetch_row(res)))
printf("sum %lld %d/%d: %s\n", ptid, year, month, row[0]);
mysql_free_result(res);
mysql_close(con);
gcc -Wall -ggdb2 -I/usr/include/mariadb -o getmouths.out getmouths.c -lmysqlclient
./getmouths.out 30422720614402293 6 2016
| 函式 | 用途 |
|---|---|
mysql_init() | 初始化 MYSQL 結構體 |
mysql_real_connect() | 建立真實連線 |
mysql_query() | 執行 SQL 陳述式 |
mysql_store_result() | 取得完整結果集(客戶端緩衝) |
mysql_fetch_row() | 逐列取得資料 |
mysql_free_result() | 釋放結果集記憶體 |
mysql_close() | 關閉連線 |
mysql_error() | 取得最近一次錯誤訊息 |
GRANT SELECT ON oss_log.* TO 'csz'@'%' IDENTIFIED BY '...';mysql_ssl_set)。PREPARE STATEMENT 參數化查詢防止 SQL Injection(本範例因動態表名無法參數化,需嚴格驗證輸入)。C 搭配 MariaDB C API 處理動態跨表查詢,靈活度遠高於純 SQL 或 Shell。掌握 mysql_store_result + 動態字串拼接 模式,可應對各類分表、分庫彙總需求。