工程优化——清理数据库表中的数据


  随着时间的推移,会往表中插入越来越多的数据,所以要对表中的一些数据进行清理,现在就来写一个工具,用来清理数据库表中的数据。

一、分析需求设计参数及准备程序的前期工作

1.分析需求设计参数

1.1 能登录指定的数据库

  (1)这个程序的主要目的是删除数据库中的表的数据,所以需求之一:能登录指定的数据库。

  (2)不同的用户有不同的账号,所以程序的参数之一就是:目的数据库的连接参数,格式为:scott/123456@snorcl11g_198

1.2.能指定待清理的表名

  (1)清理表中的数据,并不是每个表都去执行,所以需求就是:能够指定待清理的表名

  (2)每次执行这个程序,指定的表名可能不一样,所以表名作为程序的参数之一。

1.3.能指定清理数据的条件

  (1)清理表中的数据,能够指定删除的条件,不同的条件满足不同的需求。如果满足了指定的条件的数据,那么表中的数据就会被删除。

  (2)每次指定的条件可能会不一样,所以指定的删除条件作为程序的参数之一。

1.4.能在指定的时间点去执行程序

  (1)能够指定时间点去启动这个清理程序,一般在深夜或者凌晨的时候启动这个清理程序,因为这个时候对数据库的操作相对来说就比较少。并且可以指定多个时间点,比如:2点,3点,14点等。

  (2)指定的时间作为程序的参数。

1.5.合并参数

  根据分析需求,可以知道这个程序要有哪些参数,如果加上程序本身的可执行程序参数和程序运行日志文件参数,那么这个程序的参数就有6个了。

  所以将第一步的4个参数合并成一个 XML 参数。

2.参数命名以及确定格式


void _help()
{
        printf("本程序为工具程序,用于清理数据库表中的数据记录.\n");
        printf("Using:/htidc/public/bin/deleteTablesData /log/qxidc/deleteTablesData_SURFDATA.log \"<connstr>scott/123456@snorcl11g_198</connstr><tname>T_SURFDATA</tname><where>where ddatetime<sysdate-0.7</where><hourstr>11,12,13</hourstr>\"\n\n");

        printf("/log/qxidc/deleteTablesData_SURFDATA.log 程序运行日志文件。\n");

        printf("<connstr>scott/123456@snorcl11g_198</connstr> 目的数据库的连接参数。\n");

        printf("<tname>T_SURFDATA</tname> 指定待清理的表名。\n");
        printf("<where>where ddatetime<sysdate-0.7</where> 清理数据的条件。\n");
        printf("<hourstr>11,12,13</hourstr> 本程序启动的时间点,小时,时间点之间用半角的逗号分隔>开。\n");
}

3.准备写程序的前期工作

  在开始设计程序或者说写程序之前,前期工作准好

  (1)包含所需的头文件

  (2)编写好使用程序的提示信息

  (3)程序调试的一个重要的工具,贯穿程序始终——程序的运行日志 ,
所以把程序的日志文件准备好。也就是定义日志文件操作对象,打开日志文件。

  (4)屏蔽程序的中的信号,并编写处理程序退出的信号的函数

二、设计程序的基本流程

1.解析参数

  要先将 合并的 XML 参数解析出来,以便后面的步骤使用。主要是利用 GetXMLBuffer 函数。

bool _XMLToArg(const char *strArg)
{
        // 初始化参数结构体,存放解析出来的参数
        memset(&starg,0,sizeof(struct st_arg));

        // 用XML解析函数解析
        GetXMLBuffer(strArg,"connstr",starg.connstr);
        if ( strlen(starg.connstr) == 0 ) { logfile.Write("starg.connstr is null.\n"); return false; }

        GetXMLBuffer(strArg,"tname",starg.tname);
        if ( strlen(starg.tname) == 0 )
        { logfile.Write("starg.tname is null.\n"); return false; }

        GetXMLBuffer(strArg,"where",starg.where);
        if ( strlen(starg.where) == 0 )
        { logfile.Write("starg.where is null.\n"); return false; }
        
        GetXMLBuffer(strArg,"hourstr",starg.hourstr);
        if ( strlen(starg.hourstr) == 0 )
        { logfile.Write("starg.hourstr is null.\n"); return false; }

        return true;
}

2.删除表中符合条件的数据记录

(1)登录数据库

  要对表进行操作,首先要登录目的数据库。

(2)准备删除数据记录的工作——利用 rowid 和 in 提升效率

  目的是删除表中符合条件的数据记录,要涉及到删除操作。在操作数据库的时候,可以利用 rowid 来提升操作的效率。

  也就是获取符合删除条件的数据记录的 rowid ,然后利用 rowid 去删除。

  删除的时候,也可以利用 in ,一次删除多条数据。Oracle 中 in 的用法之前的文章有介绍。
(文章链接:https://blog.csdn.net/qq_43403759/article/details/116760170)

3.提交事务

三、删除函数具体实现

  这个程序的难点主要是在删除表中符合条件的数据记录,所以重点解释它即可。

1.获取 rowid

  先将符合删除条件的 rowid 查询出来。

 sqlstatement selstmt;
        selstmt.connect(&conn);

        selstmt.prepare("select rowid from %s %s",starg.tname,starg.where);

        if ( selstmt.execute() != 0 )
        {
                logfile.Write("selstmt.execute() failed.\n");
                if ( (selstmt.m_cda.rc >= 3113) && (selstmt.m_cda.rc <= 3115 ) )
                {
                        logfile.Write("数据库连接断开。\n");
                }
                return false;
        }

2.拼装一次删除多条记录的 SQL 语句

  一次删除一条记录的语句如下:

delete from t_surfdata where rowid = rowid1;

  一次删除一条记录的效率不高,可以利用 in 一次删除多条记录。

delete from t_surfdata where rowid in (rowid1,rowid2,rowid3,...,rowidn);

  为了可以多次使用删除的SQL语句,在写 SQL 的 prepare 语句时,将rowid 改为变量,变量来存放。虽然每次执行 rowid 不同,也不需要每次再去写删除的 SQL 语句了。所以删除的 SQL 语句应该改为:

delete from t_surfdata where rowid in (:1,:2,:3,...,:n);

  有一个问题又来了,如果一次要删除几百条记录,不可能手敲几百个数字吧。所以要先拼装 SQL 语句,用 for 循环拼装。

  (1)定义一个容量比较大的字符串用来存放 SQL语句。然后将SQL语句中不变的部分复制到字符串中。

char strSQL[10000];
memset(strSQL,0,sizeof(strSQL));

strcpy(strSQL,"delete from t_surfdata where rowid in (");

  在这里补充一下:strcpy 和 sprintf 的用法。strcpy 用于两个字符串之间的复制。sprintf 是将内容写到字符串中,所写的内容的数据类型是多样的,但是到了字符串中都是字符。sprintf 的作用还很方便的,可以写出或者拼写出想要的各种字符串。比如,我要将数字放到字符串中,如果不用 sprintf ,可能还要将数据类型转换,然后再放到字符串中,这样不灵活 。

  (2)将 SQL 语句不变的部分放入了,strSQL 中,那么要怎么样将 :1,:2 这些放到 strSQL 中呢。可以先将 :n sprintf 用写到到一个字符串中 strNum,然后用 strcat 将 strNum 拼接到 strQL 的内容的末尾。

int  ii;
char strNum[11];
for ( ii=0;ii<500;ii++)
{
 	memset(strNum,0,sizeof(strNum));
 	
 	if ( ii==0 ) sprintf(strNum,":%d",ii+1);
 	if ( ii >0 ) sprintf(strNum,",:%d",ii+1);
	strcat(strSQL,strNum);
}
strcat(strSQL,")");

3.绑定输入变量(rowidn)

  将 SQL 的prepare 语句拼装好了( delete from t_surfdata where rowid in (:1,:2,:3,…,:n); ),接下来就是绑定 rowidn 对应的变量。


  for (ii=0; ii<maxcounts; ii++)
  {
    delstmt.bindin(ii+1,strRowidIn[ii],50);
  }

  注意 prepare 语句是不是要多次写,绑定输入输出变量也是,有时并不需要多次绑定,绑定一次即可。

  下面的代码,输入了一个rowid的值,就绑定一个rowid的变量,这样执行多次,要多次进行绑定,并不是最好的。所以应该把绑定的代码放在(输入rowid变量的值)外面如上
在这里插入图片描述

4.赋值给输入变量

  查询符合条件的 rowid 会有多条,所以在while 循环中进行赋值。赋值就是每次从结果集中取一条记录出来。取出来的变量放到输出变量 strRowidOut 中,strRowidOut 又将内容复制给 strRowidIn。
在这里插入图片描述

5.执行删除 SQL 语句

  (1)如果取出来的记录达到每次删除的最大数目,就执行删除语句,这样就达到了一次删除多条记录的目的。

 if ( count == maxcount )
                  {
                           if ( delstmt.execute() != 0 )
                           {
                                   logfile.Write("maxcount delete %s failed.\n%s\n",starg.tname,delstmt.m_cda.message);
                                   if ( ( delstmt.m_cda.rc >= 3113 ) && ( delstmt.m_cda.rc <= 3115) )
                                   { return false; }
                                   continue;
                           }

                           count=0;
                           conn.commit();

                   }

  (2)如果达不到要一次删除的最大数目,就一条一条地删除

 if ( count < maxcount )
                {
                        delstmt.prepare("delete from %s where rowid=:1",starg.tname);

                        for ( ii = 0; ii < count; ii++ )
                        {
                                delstmt.bindin(1,strRowidIn[ii],50);
                                if (delstmt.execute() != 0 )
                                {
                                        if ( delstmt.m_cda.rc != 1 )
                                        {
                                                logfile.Write("小于maxcount delete %s failed.\n%s\n",starg.tname,delstmt.m_cda.message);
                                                 if ( ( delstmt.m_cda.rc >= 3113 ) && ( delstmt.m_cda.rc <= 3115) )
                                                        { return false; }

                                        }
                                        
                                        continue;

                                }
                        }
                }
                                        

6.提交事务

7.在指定的时间段执行删除函数

  这个删除表中的记录的程序,有一个需求就是:在指定的时间点能够去执行删除。比如说在 12点,14点等等。为什么要有这个需求呢?因为在删除表中的数据记录的时候,尽量选择没有人或者少人同时对数据库进行操作。

  怎么实现这个功能,程序前面的步骤已经将 XML 参数解析出来,02,03,04已经在 starg.hourstr 里面。

  先获取系统的当前的时间,时间的格式为:“hh24” 只需要比较小时,所以获取小时点就可以。然后用获取的时间点在 starg.hourstr 里面查找(strstr 函数),如果获取的时间点能在里面找到,就执行。
在这里插入图片描述

 char strNextStartT[11];
        while (true)
        {
                memset(strNextStartT,0,sizeof(strNextStartT));
                LocalTime(strNextStartT,"hh24");
                if ( strstr(starg.hourstr,strNextStartT) != 0 ) // 在指定的时间段启动删除
                {
                        //logfile.Write("程序启动。\n");
                        if ( _DelTabData() == false )
                                { sleep(60); }
                }
        }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值