TypeCodes

Linux C++简单实现一个批量插入的sql脚本生成工具

之前项目上隔三差五地要手工批量导入一些数据,于是自己用C++写了这个sql脚本文件的生成工具。

Linux C++简单实现一个批量插入的sql脚本生成工具

1 需求说明

批量插入多个用户收费信息,也就是用户id不同,但是包括所属区域代号、收费模式和费率值等数据是相同的。如果使用手工拼接insert语句工作量很大,而且可能出现差错,所以改用程序来生成。

只需要配置好相关参数,然后直接执行程序就可以把生成的sql脚本交给DBA处理了。

2 C++程序

代码比较简单,从main函数中可以看出主要流程是先读取配置文件(见小节3)中的数据,然后把使用|分隔的用户id进行分解。经过一系列操作后,会拼接出所有的insert语句,最终把这些数据到写入到文件流中。

这里用到了C++文件流的读写、string中字符串的查找和删除、string数据切割成vector或者map对象等。当然代码还可以做很多优化,例如参数的过滤检查、用户数过多时考虑分成多个insert语句、数据量大时使用多线程等。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
/** 
 * @FileName    linux_file_basic1_2.cpp
 * @Describe    Linux C++简单实现一个批量插入的sql脚本生成工具.
 * @Author      vfhky 2017-10-18 23:29 https://typecodes.com/cseries/genmysqlinsertsql.html
 * @Compile     g++ linux_file_basic1_2.cpp -o linux_file_basic1_2 -std=c++11
 */
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <sstream>

#include <time.h>


typedef std::map<std::string, std::string> StringMap;


//去除string中所有的空格
void TrimAllSpace( std::string &s_str )
{
    size_t index = 0;
    if( !s_str.empty())
    {
        while( (index = s_str.find(' ',index)) != std::string::npos )
        {
            s_str.erase( index, 1 );
        }
    }
}

//切割字符串
void splitToMap( const std::string &s_orginal, const std::string &s_split_pattern, StringMap &StringMap_Obj )
{
    if( s_orginal.empty() )
    {
        throw( "s_Faccount_id is empty." );
    }

    //方便截取最后一段数据
    std::string s_strs = s_orginal + s_split_pattern;

    size_t i_pos = s_strs.find(s_split_pattern);
    size_t i_size = s_strs.size();

    while( i_pos != std::string::npos )
    {
        std::string s_key_val = s_strs.substr( 0, i_pos );

        size_t i_key_pos = s_key_val.find( "=" );
        if( i_key_pos != std::string::npos )
        {
            std::string s_key = s_key_val.substr( 0, i_key_pos );
            std::string s_val = s_key_val.substr( i_key_pos+1, s_key_val.size() );

            StringMap_Obj.insert( make_pair( s_key, s_val ) );
        }
        else
        {
            throw( "config file is illegal." );
        }

        s_strs = s_strs.substr( i_pos+1, i_size );
        i_pos = s_strs.find( s_split_pattern );
    }
}

//切割字符串
void splitToVector( const std::string &s_orginal, const std::string &s_split_pattern, std::vector<std::string> &vect_str )
{
    vect_str.clear();

    if( s_orginal.empty() )
    {
        throw( "s_Faccount_id is empty." );
    }

    //方便截取最后一段数据
    std::string strs = s_orginal + s_split_pattern;

    size_t pos = strs.find(s_split_pattern);
    size_t size = strs.size();

    while( pos != std::string::npos )
    {
        std::string s_str = strs.substr(0,pos);
        TrimAllSpace( s_str );
        vect_str.push_back( s_str );
        strs = strs.substr(pos+1,size);
        pos = strs.find(s_split_pattern);
    }
}

void GetTime( std::string &s_date )
{
    time_t t = time(NULL);
    char p_date[20] = {0x00};
    strftime( p_date, sizeof(p_date), "%Y%m%d %H:%M:%S", localtime(&t) );
    s_date = p_date;
}



class CGenSqlFile
{
public:
    CGenSqlFile( const std::string &s_config_file ): s_config_file( s_config_file )
    {
        ReadFile();
    }

    virtual ~CGenSqlFile(){}


    //读取每行的数据,然后进行处理
    void ReadFile()
    {
        std::ifstream inFile;

        //以二进制可读的方式打开文件,也可以使用: inFile.open( s_config_file.data() );
        inFile.open( s_config_file.c_str(), std::ios::in | std::ios::binary );

        //Checks if the file stream has an associated file.
        if( !inFile.is_open() )
        {
            inFile.close();
            throw( "Cannot open the config file." );
        }

        //每一行的数据
        std::string s_line_buf;

        //读取一行内容    getline( inFile, s_line_buf )
        while( getline( inFile, s_line_buf, inFile.widen('\n') ) )
        {
            if( s_line_buf.empty() || s_line_buf[0] == '#' )
            {
                continue;
            }

            //开始数据处理
            splitToMap( s_line_buf, "&", StringMap_Obj );
        }

        //关闭文件流
        inFile.close();
    }

    //打印所有的配置数据
    void ShowAllConf() const
    {
        std::cout << "=================== Config data begin. ===================" << std::endl;
        for( auto iter : StringMap_Obj )
        {
            std::cout << "[" << iter.first << "]=[" << iter.second << "]" << std::endl;
        }
        std::cout << "=================== Config data end. ===================" << std::endl;
    }

    std::string GenSql()
    {
        const std::string s_genonesql_comm = GenOneSqlComm();

        //所有的商户/渠道id
        std::vector<std::string> vect_Faccount_id;
        splitToVector( StringMap_Obj["Faccount_id"], "|", vect_Faccount_id );

        //拼装sql
        std::string s_sql_content;
        for( auto iter : vect_Faccount_id )
        {
            s_sql_content += GenOneSql( iter, s_genonesql_comm );
        }

        //把末尾的逗号去掉
        std::string s_sql_contents = s_sql_content.erase( s_sql_content.length()-2, 2 );

        //当前日期
        std::string s_date;
        GetTime( s_date );
        std::string s_sql_illustartion = "-- --------------------------------------------------------------------\n"
                                         "-- Auto Generated on " + s_date + " --------------------------------\n"
                                         "-- --------------------------------------------------------------------\n";

        return ( s_sql_illustartion + StringMap_Obj["INSERT_HEADER"] + "\n" + s_sql_contents + "\n;\n\n\n" );
    }


    //生成SQL数据
    std::string GenOneSql( const std::string &s_Faccount_id, const std::string &s_genonesql_comm )
    {
        return ( "( '" + s_Faccount_id + s_genonesql_comm );
    }

    std::string GenOneSqlComm()
    {
        std::string s_sql = "', '" + StringMap_Obj["Fpay_channel_id"] + "', '" + StringMap_Obj["Fpay_method"] + "', '" + StringMap_Obj["Frate_mode"] + "', '" + StringMap_Obj["Frate_value"] + "', '" + StringMap_Obj["Frate_value_base"] + "', '1'),\n";

        return s_sql;
    }

    void OpenFile()
    {
        //追加
        if( "1" == StringMap_Obj["Ffile_mode"] )
        {
            ostream.open( StringMap_Obj["sql_file"].c_str(), std::ofstream::out | std::ofstream::binary | std::ofstream::app );
        }
        //覆盖
        else if( "2" == StringMap_Obj["Ffile_mode"] )
        {
            ostream.open( StringMap_Obj["sql_file"].c_str(), std::ofstream::out | std::ofstream::binary );
        }
        else
        {
            throw( "Ffile_mode is illegal." );
        }


        if( !ostream.is_open() )
        {
            ostream.close();
            throw( "Cannot open the file." );
        }
    }

    void CloseFile()
    {
        ostream.close();
    }

    //获取配置数据
    const std::string GetConfByKey( std::string &s_config_key ) const
    {
        StringMap::const_iterator iter = StringMap_Obj.find( s_config_key );
        if( iter != StringMap_Obj.end() )
        {
            return iter->second;
        }
        else
        {
            return "";
        }
    }

    //把数据写入到文件流中
    void WriteToFile( const std::string &s_file_contents )
    {
        ostream << s_file_contents;
    }

protected:
    StringMap StringMap_Obj;


private:
    std::string s_config_file;
    std::ofstream ostream;

};


int main( const int argc, const char * const * argv )
{
    static const std::string s_config_file = "./LINUX_FILE_BASIC1_2.txt";


    try
    {
        CGenSqlFile t( s_config_file );

        //显示所有的配置
        t.ShowAllConf();

        //打开文件
        t.OpenFile();

        //生成SQL数据
        std::string s_sql_contents = t.GenSql();

        //写入到文件
        t.WriteToFile( s_sql_contents );

        //关闭文件
        t.CloseFile();

        std::string s_config_key = "sql_file";
        std::cout << "\nGenerate sql file=[" << t.GetConfByKey( s_config_key ) << "] success." << std::endl;
    }
    catch( const char *p_errstr )
    {
        std::cout << p_errstr << std::endl;
    }

    return 0;
}

3 配置文件

上面的C++程序会读取这个linux_file_basic1_2.txt的配置文件,可以使用#符号给一些必要的数据配备注释文字。

需要说明的是Ffile_mode参数表示sql文件的写入方式,当值为2时表示每次都把生成的数据覆盖原文件中的数据;为1时表示支持把多次生成的SQL数据写入到同一个SQL脚本文件中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#  
# @FileName    linux_file_basic1_2.txt
# @Describe    Linux C++简单实现一个批量插入的sql脚本生成工具的配置文件.
# @Author      vfhky 2017-10-18 23:29 https://typecodes.com/cseries/genmysqlinsertsql.html



# 用户账号id,多个账号使用 | 符号分隔
Faccount_id=10001342|  10001447

# 所属区域代号
Fpay_channel_id=GDSZ01

# 支付方式:
# 支付宝服务窗支付      ALIPAY.JSAPI
# 支付宝刷卡         ALIPAY.MICROPAY
# 支付宝扫码支付       ALIPAY.NATIVE
# 
# 微信公众号         WXPAY.JSAPI
# 微信刷卡支付            WXPAY.MICROPAY
# 微信扫码支付            WXPAY.NATIVE
Fpay_method=WXPAY.NATIVE

# 收费模式,1固定比例,2单笔计费
Frate_mode=1

# 费率值,单位:1/10000
Frate_value=50

# 基本费用,单位:分
Frate_value_base=0


# 生成的sql文件名
sql_file=user_account.sql

# 生成的sql文件模式,1追加,2覆盖
Ffile_mode=2

# insert语句的头部
INSERT_HEADER=INSERT INTO `USER_ACCOUNT_DB`.`t_mch_rate` ( `Faccount_id`, `Fpay_channel_id`, `Fpay_method`, `Frate_mode`, `Frate_value`, `Frate_value_base`, `Fstatus`) VALUES

4 编译执行

使用《Linux C/C++工程中可生成ELF、动/静态库文件的通用Makefile》文中的Makefile编译:

Linux C++简单实现一个批量插入的sql脚本生成工具

然后执行生成MySql脚本如下:

Linux C++简单实现一个批量插入的sql脚本生成工具

打赏支持

Comments »