【shell】毎日指定時間内に動的にSQLを生成して実行する
21:00:00~23:59:59 の間、処理を繰り返したい場合の例
resident_main.sh
#!/bin/bash ############################################################ # # 毎日指定時間内にSQL実行(常駐) # (c) 2018 mikan # ※使用にあたっては利用者の自己責任でお願いします。 # 使い方: # 例)nohup bash resident_main.sh & # # ログファイル名:yyyymmdd_HHMMSS_sql_main.log # ############################################################ ### ----- 初期処理 LOG_FILE_NAME=`date +%Y%m%d_%H%M%S`_sql_main.log ### ----- 終了フラグ END_FLG="end.flg" ### ----- スリープ時間(1~60秒程度で設定) SLEEP_TIME=2 ### ----- 進捗表示用(ASCIIコードの16進) debug用 #CODE=0 #debug SYSTEM_DATE=`date +%Y/%m/%d_%H:%M:%S` echo "※21:00~17:59の間はスリープします" >> $LOG_FILE_NAME echo "※中断したい場合は、touch $END_FLG" >> $LOG_FILE_NAME echo "[$SYSTEM_DATE]: ===== SQL実行メイン(常駐) - 開始 =====" >> $LOG_FILE_NAME # ----- 終了フラグを削除 if [ -f `pwd`/$END_FLG ] then rm `pwd`/$END_FLG fi ### ----- 処理完了日 DAY_COMPLETE=00000000 ### ----- DAY_COMPLETE を外部ファイルへ出力(グローバル変数として使用) echo "DAY_COMPLETE=$DAY_COMPLETE;export DAY_COMPLETE" > day_complete.dat ### ----- メインループ while : do # ----- ※00:00~20:59の間はスリープ while : do # ----- 終了チェック if [ -f `pwd`/$END_FLG ] then SYSTEM_DATE=`date +%Y/%m/%d_%H:%M:%S` echo "[$SYSTEM_DATE]: $END_FLG 検出!" >> $LOG_FILE_NAME echo echo "[$SYSTEM_DATE]: ===== SQL実行メイン(常駐) - 終了 =====" >> $LOG_FILE_NAME echo exit 0; fi # ----- 時刻チェック S_TIME=`date +%H` # 本番 (時) case $S_TIME in 21 | 22 | 23 ) #21時~23時(21:00:00~23:59:59)の場合はループを抜ける break;; * ) #実行時間外 sleep $SLEEP_TIME continue;; esac sleep $SLEEP_TIME done # ----- DAY_COMPLETE を取得 . ./day_complete.dat # ----- システム日付(yyyymmdd)を取得 DAY_TODAY=`date +%y%m%d` #----- 1日1回だけSQL実行を呼び出す if [ $DAY_TODAY != $DAY_COMPLETE ] then # ----- SQL実行呼び出し SYSTEM_DATE=`date +%Y/%m/%d_%H:%M:%S` echo "[$SYSTEM_DATE]: SQL実行を呼び出し" >> $LOG_FILE_NAME bash ./sqlexe.sh # ----- 処理完了日へシステム日付(yyyymmdd)を取得 DAY_COMPLETE=`date +%y%m%d` # ----- DAY_COMPLETE を外部ファイルへ出力(グローバル変数として使用) echo "DAY_COMPLETE=$DAY_COMPLETE;export DAY_COMPLETE" > day_complete.dat SYSTEM_DATE=`date +%Y/%m/%d_%H:%M:%S` echo "[$SYSTEM_DATE]: DAY_COMPLETE(処理完了日)=$DAY_COMPLETE" >> $LOG_FILE_NAME fi sleep $SLEEP_TIME done # ----- 理論上ここを通ることはない exit 1
resident_sql.sh
#!/bin/bash ############################################################ # # 動的にSQLを生成して実行 # (c) 2018 mikan # ※使用にあたっては利用者の自己責任でお願いします。 # # ログファイル名:yyyymmdd_HHMMSS_sql_exec.log # # SQL文だけを抽出する例 # grep -v ": " 20180515_215302_sql_exec.log # # SQL文以外の実行ログを抽出する例 # grep ": " 20180515_215402_sql_exec.log # ############################################################ ### ----- ログファイル LOG_FILE_NAME=`date +%Y%m%d_%H%M%S`_sql_exec.log ### ----- SQL実行間隔(1~5秒程度で設定) SLEEP_TIME=5 ### ----- 終了フラグ END_FLG="end.flg" ### ----- パラメータファイル取込 . ./param.txt echo SYSTEM_DATE=`date +%Y/%m/%d_%H:%M:%S` echo "[$SYSTEM_DATE]: ===== SQL実行 - 開始 =====" ### ----- メインループ for i in "${dateArray[@]}"; do # ----- 終了チェック if [ -f `pwd`/$END_FLG ] then echo "$END_FLG 検出!" echo SYSTEM_DATE=`date +%Y/%m/%d_%H:%M:%S` echo "[$SYSTEM_DATE]: ===== SQL実行 - 終了 =====" >> LOG_FILE_NAME echo exit 0; fi # ----- 時刻チェック(18,19,20時以外は終了) S_TIME=`date +%H` # 時 case $S_TIME in 21 | 22 | 23 ) #21時~23時(21:00:00~23:59:59)の場合は処理 ;; * ) #実行時間外(処理終了) break;; esac # ----- 各項目を取得 data=(${i[@]}) EXEC_DAY=${data[0]} DATE_STR=${data[1]} DATE_END=${data[2]} # ----- インクリメント COUNT=$(( COUNT + 1 )) #初期値はゼロ(初期化不要) # ----- 処理日チェック S_TIME=`date +%Y%m%d` echo "[$COUNT]: システム日付[$S_TIME] パラメータ[$EXEC_DAY $DATE_STR $DATE_END]" >> $LOG_FILE_NAME if [ $S_TIME != $EXEC_DAY ] then echo "[$COUNT]: 本日対象外の処理日行($EXEC_DAY)はスキップ" >> $LOG_FILE_NAME continue fi # ----- 条件表示 S_YEAR=${DATE_STR:0:4} S_MON=${DATE_STR:4:2} S_DAY=${DATE_STR:6:2} S_NO=${DATE_STR:8} E_YEAR=${DATE_END:0:4} E_MON=${DATE_END:4:2} E_DAY=${DATE_END:6:2} E_NO=${DATE_END:8} # ----- SQLファイル削除 if [ -f `pwd`/tmp.sql ] then rm `pwd`/tmp.sql fi # ----- SQLファイル作成ループ ※SQLは目的に合わせて書き換えてください for j in "${tableArray[@]}"; do # ----- 各項目を取得 tableName=(${j[@]}) # ----- SQL echo "DELETE from $tableName" >> tmp.sql echo " WHERE 項目名1 BETWEEN '$DATE_STR' AND '$DATE_END'" >> tmp.sql echo " AND 項目2 IN ('AAA','BBB','CCC');" >> tmp.sql echo "commit work;" >> tmp.sql done; # ----- SQL実行 ※xxDBは環境に合わせて書き換えてください SYSTEM_DATE=`date +%Y/%m/%d_%H:%M:%S` echo >> $LOG_FILE_NAME echo "----- SQL start [$COUNT][$SYSTEM_DATE] -----" >> $LOG_FILE_NAME date >> $LOG_FILE_NAME rdbexecsql -s xxDB -i `pwd`/tmp.sql >> $LOG_FILE_NAME date >> $LOG_FILE_NAME SYSTEM_DATE=`date +%Y/%m/%d_%H:%M:%S` echo "----- SQL end [$COUNT][$SYSTEM_DATE] -----" >> $LOG_FILE_NAME echo >> $LOG_FILE_NAME # ----- 終了チェック if [ -f `pwd`/$END_FLG ] then echo "$END_FLG 検出!" >> $LOG_FILE_NAME break; fi # ----- スリープ sleep $SLEEP_TIME done; SYSTEM_DATE=`date +%Y/%m/%d_%H:%M:%S` echo "[$SYSTEM_DATE]: ===== SQL実行 - 終了 =====" >> LOG_FILE_NAME echo exit 0
param.txt
### ----- 処理完了日(debug等特殊な場合を除き通常は"00000000"を設定) DAY_COMPLETE=00000000;export DAY_COMPLETE ### ----- 対象テーブル tableArray=( 'xxxx.テーブル1' 'xxxx.テーブル2' 'xxxx.テーブル3' ) export tableArray ### ----- BETWEEN 条件 # # システム日付と処理日が一致する行だけが # 実行対象となります # 一致しない行は、スキップします # #-処理日- -----開始------ -----終了------ dateArray=( '20180516 2008010100000 2008123199999' '20180516 2009010100000 2009123199999' '20180517 2010010100000 2010123199999' '20180517 2011010100000 2011123199999' '20180518 2012010100000 2012123199999' '20180518 2013010100000 2013123199999' ) export dateArray
【C言語】SymfowareのSELECT結果をCSV形式に変換する
// // SymfowareのSELECT結果をCSV形式へ変換 ※Solarisで動作確認済(1行32kまで対応) // (c) 2018 mikan // ※使用にあたっては利用者の自己責任でお願いします。 // // 使い方:SQLtoCSV input [output] // 例)SQLtoCSV select.log // 例)SQLtoCSV select.log select.txt // // おまけ機能)INSERT SQL を作成する方法 // SQLtoCSV select.log select.txt -insert // #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #define BUFF_MAX 32768 // 入力ファイルの1行の最大サイズ #define FIELD_MAX 1024 // 項目の最大数 int MakeCsvHead(char *, char *, int); int MakeCsvData(char *, char *, int, int, int); char fullpath1[2048]; char fullpath2[2048]; int main(int argc, char **argv) { int len; int head_count; int data_count; int dc_flag; int sp_flag; int insert_flag; struct stat st; char work[1024]; dc_flag = 1; sp_flag = 0; insert_flag = 0; if(argc <= 1 || argc >= 4) { if(argc == 4 && strcmp(argv[3], "-insert") == 0) { printf("-insert オプション検出! insert.sql を作成します\n"); insert_flag = 1; } else { printf("パラメータが不正です\n"); return -1; } } // ----- 入出力ファイル名の取得 strcpy(fullpath1, argv[1]); // ----- 入力ファイルのチェック if(stat(fullpath1, &st) != 0) { printf("%s\n入力ファイルが存在しません\n", fullpath1); return -1; } // ----- 出力ファイル名の自動生成 if(argc == 3) { strcpy(fullpath2, argv[2]); } else { strcpy(fullpath2, fullpath1); strcat(fullpath2, ".txt"); } // ----- 入出力ファイル名のチェック if(strcmp(fullpath1, fullpath2) == 0) { printf("変換元/変換先ファイル名が同じ\n"); return -1; } printf("SELECT結果変換 - 開始\n"); // ----- CSVファイル作成 head_count = MakeCsvHead(fullpath1, fullpath2, dc_flag); data_count = MakeCsvData(fullpath1, fullpath2, dc_flag, sp_flag, insert_flag); printf("SELECT結果変換 - 完了っ[%d件] [%d項目]\n", data_count, head_count); } // NUMERICチェック(先頭が'+'または'-'で、 // 後続が全て数値('0'-'9')または'.'で、 // '.'が1個の場合、0を返す) int numeric_check(char *str) { int i; int len; int count; len = 0; count = 0; // 先頭が'+'でも'-'でもない場合は、NUMERIC型ではない if(str[0] != '-' && str[0] != '+') { return -1; } // 全桁チェック len = strlen(str); for(i=1; i < len ; i++) { if(str[i] == 0x00) { break; } // 数値でも'.'でもない場合は、NUMERIC型ではない if(str[i] < 0x30 || str[i] > 0x39) { if(str[i] == '.') { count ++; } else { return -1; } } } // '.'が1個でない場合は、NUMERIC型ではない if(count != 1) { return -1; } // 数値('+9999.' or '-9999.' or '+9999.9999' or '-9999.9999') return 0; } int DeleteSpace(char *str1, char *str2, int size) { int i; int len; strcpy(str2, str1); for(i=0; i<size; i++) { if(str1[i] == 0x20) { strcpy(str2, &str1[i+1]); } else { break; } } len = strlen(str2); for(i=len-1; i>0; i--) { if(str2[i] == 0x20) { str2[i] = 0x00; } else { break; } } len = strlen(str2); return len; } int CheckRecord(char *buff, char *tablename) { int i; char *p; // ----- テーブル名の取得(xxxx を 環境に合わせて書き換えてください) if((p = strstr(buff, "xxxx.")) != NULL) { for(i=0; ; i++) { if(p[i] == ' ' || p[i] == 0x0a) { memset(tablename, 0x00, sizeof(tablename)); memcpy(tablename, p, i); break; } } } if(strncmp(buff, "Number of records", 17) == 0) { return -1; } if(strncmp(buff, "SQLSTATE", 8) == 0) { return -1; } if(strncmp(buff, "SQLMSG", 6) == 0) { return -1; } if(strncmp(buff, "SELECT", 6) == 0) { return -1; } if(strncmp(buff, "select", 6) == 0) { return -1; } if(strncmp(buff, "Select", 6) == 0) { return -1; } return 0; } // ----- CSVヘッダ作成処理 int MakeCsvHead(char *in_file, char *out_file, int dc_flag) { FILE *fp1, *fp2; // ファイルポインタ int i; // インデックス int len; // 文字列のサイズ取得変数 int count_record; // 出力レコード数 int count_field; // 出力フィールド数 char buff[BUFF_MAX]; // 入力ファイルの1行読込エリア char work[BUFF_MAX]; // ワークエリア // ----- パラメータチェック if(in_file == NULL || out_file == NULL) { return(-1); } // ----- 入力ファイルオープン(読込み専用) fp1 = fopen(in_file, "r"); if(fp1 == NULL) { return(-1); } // ----- 出力ファイルオープン(上書き) fp2 = fopen(out_file, "w"); if(fp2 == NULL) { fclose(fp1); return(-1); } // ----- 初期化 count_record = 0; count_field = 0; // ----- 入力ファイルの先頭レコードぶん繰り返し while(1) { if(count_field >= FIELD_MAX) { break; } // ----- 1行読込(EOFで抜ける) memset(buff, 0x00, sizeof(buff)); if(fgets(buff, BUFF_MAX-1, fp1) == NULL) { break; } // ----- 読み飛ばし if(CheckRecord(buff, work) != 0) { continue; } // ----- 行末の改行コードを取る len = strlen(buff); buff[len-1] = NULL; // ----- <<xxx>> を探す。(レコードの始まり) if(strncmp(buff, "<<", 2) == 0) { count_record ++; if(count_record > 1) { break; } continue; } // ----- ファイル出力(ヘッダ部) for(i = 0; i<BUFF_MAX; i ++) { if(buff[i] == ':') { count_field ++; if(count_field > 1) { fprintf(fp2, ","); } buff[i]=0x00; // 前後の空白削除 len = strlen(buff); memset(work, 0x00, sizeof(work)); DeleteSpace(buff, work, len); // ヘッダは強制的にダブルコーテーションなしとする fprintf(fp2, "%s", work); break; } } } fprintf(fp2, "\n"); // ----- ファイルクローズ fclose(fp1); fclose(fp2); return(count_field); } // ----- CSVデータ作成処理 int MakeCsvData(char *in_file, char *out_file, int dc_flag, int sp_flag, int insert_flag) { FILE *fp1, *fp2; // ファイルポインタ FILE *fp3; // ファイルポインタ int i; // インデックス int len; // 文字列のサイズ取得変数 int count_record; // 出力レコード数 int count_field; // 出力フィールド数 char buff[BUFF_MAX]; // 入力ファイルの1行読込エリア char work[BUFF_MAX]; // ワークエリア char tname[BUFF_MAX]; // テーブル名 // ----- パラメータチェック if(in_file == NULL || out_file == NULL) { return(-1); } // ----- 入力ファイルオープン(読込み専用) fp1 = fopen(in_file, "r"); if(fp1 == NULL) { return(-1); } // ----- 出力ファイルオープン(追記) fp2 = fopen(out_file, "a"); if(fp2 == NULL) { fclose(fp1); return(-1); } if(insert_flag == 1) { // ----- 出力ファイルオープン fp3 = fopen("insert.sql", "w"); if(fp3 == NULL) { fclose(fp1); fclose(fp2); return(-1); } } // ----- 初期化 count_record = 0; count_field = 0; // ----- 入力ファイルの全レコードぶん繰り返し while(1) { if(count_field >= FIELD_MAX) { break; } // ----- 1行読込(EOFで抜ける) memset(buff, 0x00, sizeof(buff)); if(fgets(buff, BUFF_MAX-1, fp1) == NULL) { break; } // ----- 読み飛ばし if(CheckRecord(buff, tname) != 0) { continue; } // ----- 行末の改行コードを取る len = strlen(buff); buff[len-1] = NULL; // ----- <<xxx>> を探す。(レコードの始まり) if(strncmp(buff, "<<", 2) == 0) { count_record ++; count_field = 0; if(count_record > 1) { fprintf(fp2, "\n"); } if(insert_flag == 1) { if(count_record >= 2) { fprintf(fp3, ");\nCOMMIT WORK;\n"); fprintf(fp3, "INSERT INTO %s VALUES (", tname); } else { fprintf(fp3, "INSERT INTO %s VALUES (", tname); } } continue; } // ----- ファイル出力(データ部) for(i = 0; i<BUFF_MAX; i ++) { if(buff[i] == ':') { count_field ++; if(count_field > 1) { fprintf(fp2, ","); if(insert_flag == 1) { fprintf(fp3, ","); } } // 前後の空白削除 if(sp_flag == 1) { len = strlen(&buff[i+1]); memset(work, 0x00, sizeof(work)); DeleteSpace(&buff[i+1], work, len); } else { strcpy(work, &buff[i+1]); } if(dc_flag == 1) { if(numeric_check(work) != 0) { fprintf(fp2, "\"%s\"", work); if(insert_flag == 1) { fprintf(fp3, "'%s'", work); } } else { fprintf(fp2, "%s", work); if(insert_flag == 1) { fprintf(fp3, "%s", work); } } } else { fprintf(fp2, "%s", work); if(insert_flag == 1) { fprintf(fp3, "%s", work); } } break; } } } fprintf(fp2, "\n"); if(insert_flag == 1) { fprintf(fp3, ");\nCOMMIT WORK;\n"); } // ----- ファイルクローズ fclose(fp1); fclose(fp2); if(insert_flag == 1) { fclose(fp3); } return(count_record); }