【C/C++】项目_6_txt/xml文件数据处理入库(psurfdata.cpp,_shqx.h),工程组织与优化(表空间),数据结构设计(PowerDesigner)

@TOC


1.txt/xml文件入表/自身调度:_shqx.h,结构体内容

在这里插入图片描述
create table T_SURFDATA
(
   obtid      char(5)  not null,
   ddatetime  date     not null,
   t          number(5)    null,
   p          number(5)    null,
   u          number(3)    null,
   wd         number(3)    null,
   wf         number(4)    null,
   r          number(4)    null,
   vis        number(6)    null,
   crttime    date         null,
   keyid      number(15)   null,
   primary key(obtid,ddatetime)
);
select tname from tab
select * from T_SURFDATA
select * from T_SURFDATA order by keyid --不是group by
truncate table T_SURFDATA
CREATE SEQUENCE seq_surfdata INCREMENT BY 1 START WITH 1
select count(*) from T_SURFDATA
select * from user_tab_columns where table_name='T_SURFDATA'
DESC T_SURFDATA --sqlplus中才有用
//makefile
#oracle头文件路径
ORAINCL = -I$(ORACLE_HOME)/rdbms/public

# oracle库文件路径
ORALIB =  -L$(ORACLE_HOME)/lib -L.

# oracle的oci库
ORALIBS = -lclntsh

CC=g++
CFLAGS = -g -Wno-write-strings -Wno-unused-variable

all:crtsurfdata libftp.a demo18 ftpgetfile psurfdata

libftp.a:ftplib.c
        gcc -c -o libftp.a ftplib.c

demo18:demo18.cpp _ftp.h _ftp.cpp libftp.a
        g++ $(CFLAGS) -o demo18 demo18.cpp _public.cpp _ftp.cpp libftp.a

ftpgetfile:ftpgetfile.cpp _ftp.h _ftp.cpp libftp.a
        $(CC) $(CFLAGS) -o ftpgetfile ftpgetfile.cpp _public.cpp _ftp.cpp libftp.a
        cp ftpgetfile ../bin/.

crtsurfdata:crtsurfdata.cpp _public.h _public.cpp
        $(CC) $(CFLAGS) -o crtsurfdata crtsurfdata.cpp _public.cpp
        cp crtsurfdata ../bin/.

psurfdata:psurfdata.cpp _public.h _public.cpp
        g++ $(CFLAGS) -o psurfdata psurfdata.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _public.cpp _ooci.cpp
        cp psurfdata ../bin/.
        
clean:
        rm -rf crtsurfdata demo18 ftpgetfile libftp psurfdata
//psurfdata.cpp未封装成类
//本程序用于处理全国气象站点观测的分钟数据,并保存到数据库的T_SURFDATA表中。txt文件里面内容以逗号分隔
#include "_public.h"
#include "_ooci.h"
// 全国气象站点分钟观测数据结构
struct st_surfdata
{
  char obtid[11];      // 站点代码
  char ddatetime[21];  // 数据时间:格式yyyy-mm-dd hh:mi:ss。
  int  t;              // 气温:单位,0.1摄氏度
  int  p;              // 气压:0.1百帕
  int  u;              // 相对湿度,0-100之间的值。
  int  wd;             // 风向,0-360之间的值。
  int  wf;             // 风速:单位0.1m/s
  int  r;              // 降雨量:0.1mm
  int  vis;            // 能见度:0.1米
};
CLogFile logfile;
CDir Dir;
// 处理数据文件
bool _psurfdata();
connection conn;
void EXIT(int sig);

int main(int argc,char *argv[])
{
  if (argc!=5)
  {
    printf("\n本程序用于处理全国气象站点观测的分钟数据,并保存到数据库的T_SURFDATA表中。\n");
    printf("/oracle/lian/qxidc/bin/psurfdata 数据文件存放的目录 日志文件名 数据库连接参数 程序运行时间间隔\n");
    printf("例如:/oracle/lian/qxidc/bin/psurfdata /oracle/lian/qxidc/tmp /oracle/lian/qxidc/log/psurfdata.log scott/tiger@snorcl11g_138 10\n");
    return -1; //失败跳出main函数,必须在装有oracle主机中运行,不然_ooci中路径报错
  }
  // 关闭全部的信号和输入输出
  CloseIOAndSignal();
  // 处理程序退出的信号
  signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
  if (logfile.Open(argv[2],"a+")==false)
  {
    printf("打开日志文件失败(%s)。\n",argv[2]); return -1;
  }
  logfile.Write("程序启动。\n");
  while (true)
  {
//////////////////////////////////////////////////////1.扫描数据文件存放的目录
    //logfile.Write("开始扫描目录。\n"); //打开目录拿到文件名放入string型容器里,需排序的话就把这容器排序(先生成先处理)
    if (Dir.OpenDir(argv[1],"SURF_ZH_*.txt",1000,true,true)==false) //OpenDir第四个参数是否打开子目录,第五个参数是否排序
    {
      logfile.Write("Dir.OpenDir(%s) failed.\n",argv[1]); sleep(atoi(argv[4])); continue;
    }

///////////////////////////////////////////////////2.逐个处理目录中的数据文件
    while (true)
    {
      if (Dir.ReadDir()==false) break; //false为读完了
      //logfile.Write("%s,%s,%s,%d,%s,%s,%s,%s\n",Dir.m_DirName,Dir.m_FileName,Dir.m_FullFileName,Dir.m_FileSize,Dir.m_ModifyTime,Dir.m_CreateTime,Dir.m_AccessTime);
          
////////////////////////////////////////////////3.连接数据库
      if (conn.m_state==0)  //0未连接
      {
        if (conn.connecttodb(argv[3],"Simplified Chinese_China.ZHS16GBK")!=0)
        {
          logfile.Write("connect database(%s) failed.\n%s\n",argv[3],conn.m_cda.message); break;
        // logfile.Write("连接数据库成功。\n");
        }
      }
    
///////////////////////////////////////////////4.处理入库
      logfile.Write("开始处理文件%s...",Dir.m_FileName); //开头有时间
      if (_psurfdata()==false) 
      {
        logfile.WriteEx("失败。\n"); break;
      }      
      logfile.WriteEx("成功。\n"); //不写时间,不换行,显示屏一行放不下
    }
    // 断开与数据库的连接
    if (conn.m_state==1) conn.disconnect(); 
    sleep(atoi(argv[4]));
  }
  return 0;
}
void EXIT(int sig)
{
  logfile.Write("程序退出,sig=%d\n\n",sig);
  exit(0);
}
      
//////////////////////////////////////5.处理入库
bool _psurfdata()
{
  // 打开文件
  CFile File;
  if (File.Open(Dir.m_FullFileName,"r")==false)  //Dir的成员变量m_FullFileName也叫属性
  {
    logfile.Write("(File.Open(%s) failed.\n",Dir.m_FullFileName); return false;
  }
  char strBuffer[301];
  CCmdStr CmdStr;
  struct st_surfdata stsurfdata;//定义stsurfdata结构体变量
  int iccount=0;  //int型初始化
  sqlstatement stmtsel(&conn);
  stmtsel.prepare("select count(*) from T_SURFDATA where obtid=:1 and ddatetime=to_date(:2,'yyyy-mm-dd hh24:mi:ss')");
  stmtsel.bindin( 1, stsurfdata.obtid,5); // 定义的是字符串不用&
  stmtsel.bindin( 2, stsurfdata.ddatetime,19);
  stmtsel.bindout(1,&iccount); //select count(*)返回行数即一个数字赋给iccount这个变量,这个数字也是1列,所以bindout中1对应这列
  //select * 返回多行多列的结果集,bindin绑定选择行,bindout绑定选择列  
  sqlstatement stmtins(&conn);
  stmtins.prepare("insert into T_SURFDATA(obtid,ddatetime,t,p,u,wd,wf,r,vis) values(:1,to_date(:2,'yyyy-mm-dd hh24:mi:ss'),:3,:4,:5,:6,:7,:8,:9)");
  stmtins.bindin( 1, stsurfdata.obtid,5); //1对应:1  //,crttime,keyid  //values里可加:,sysdate,SEQ_SURFDATA.nextval
  stmtins.bindin( 2, stsurfdata.ddatetime,19);
  stmtins.bindin( 3,&stsurfdata.t);
  stmtins.bindin( 4,&stsurfdata.p);
  stmtins.bindin( 5,&stsurfdata.u);
  stmtins.bindin( 6,&stsurfdata.wd);
  stmtins.bindin( 7,&stsurfdata.wf);
  stmtins.bindin( 8,&stsurfdata.r);
  stmtins.bindin( 9,&stsurfdata.vis);
  //读取文件中的每一行记录并写入数据库的表中
  while (true)
  {
    memset(strBuffer,0,sizeof(strBuffer));
    if (File.Fgets(strBuffer,300,true)==false) break; //Fgets从文件中读取一行放入strBuffer,第三个参数为是否删除最后空格
    // logfile.Write("%s\n",strBuffer); strBuffer内容为58436,2020-02-09 12:11:00,32.4,1006.9,99,249,11.8,0.2,10107.3
    CmdStr.SplitToCmd(strBuffer,",",true); //以,号拆分成9个片段存入CmdStr中
    if (CmdStr.CmdCount()!=9)
    {
      logfile.Write("%s\n",strBuffer); continue;
    }
    memset(&stsurfdata,0,sizeof(struct st_surfdata)); //sizeof里一般用结构体类型,传变量名的话若是指针就不好了
    CmdStr.GetValue(0,stsurfdata.obtid,5);      // 站点代码 赋值给stsurfdata.obtid
    CmdStr.GetValue(1,stsurfdata.ddatetime,19); // 数据时间:格式yyyy-mm-dd hh:mi:ss。
    double dtmp=0;
    CmdStr.GetValue(2,&dtmp); stsurfdata.t=(int)(dtmp*10);  // 气温:单位,0.1摄氏度
    CmdStr.GetValue(3,&dtmp); stsurfdata.p=(int)(dtmp*10);  // 气压:0.1百帕
    CmdStr.GetValue(4,&stsurfdata.u);  // 相对湿度,0-100之间的值。
    CmdStr.GetValue(5,&stsurfdata.wd); // 风向,0-360之间的值。
    CmdStr.GetValue(6,&dtmp); stsurfdata.wf=(int)(dtmp*10);  // 风速:单位0.1m/s
    CmdStr.GetValue(7,&dtmp); stsurfdata.r=(int)(dtmp*10);   // 降雨量:0.1mm
    CmdStr.GetValue(8,&dtmp); stsurfdata.vis=(int)(dtmp*10); // 能见度:0.1米
    if (stmtsel.execute() != 0)
    {
      logfile.Write("stmtsel.execute() failed.\n%s\n%s\n",stmtsel.m_sql,stmtsel.m_cda.message); 
      if ( (stmtsel.m_cda.rc>=3113) && (stmtsel.m_cda.rc<=3115) ) return false; 
      //3113-3115是数据库连接失败
      continue;
    }
    iccount=0;
    stmtsel.next(); //把"select count(*)....."这个sql查询结果一次一次取结果集赋给绑定输出的变量即iccount
    //这里比较特殊刚好一行一列即一个数字
    if (iccount>0) continue; //有记录不执行插入
    // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。下面是打印出失败信息,execute()都会执行
    if (stmtins.execute() != 0)
    { //往数据库里插入,如果记录存在不应该退出,应该跳过已存在的记录
      if (stmtins.m_cda.rc!=1) //!=1才是真正的失败,=1是主键冲突,若是主键冲突可以跳过不入库不要返回失败
      {
        logfile.Write("%s\n",strBuffer);
        logfile.Write("stmtins.execute() failed.\n%s\n%s\n",stmtins.m_sql,stmtins.m_cda.message); 
        if ( (stmtins.m_cda.rc>=3113) && (stmtins.m_cda.rc<=3115) ) return false;
      }
    }
  }
 //处理生成数据就是先扫描目录,读取目录下文件。读到文件名后打开一文件,一行一行解析出数据插入数据库表里,关闭文件,提交事务
  conn.commit();
  // 关闭文件指针,并删除文件,不删除会重复读取
  File.CloseAndRemove(); 
  return true;
}

在这里插入图片描述

在这里插入图片描述

vi psurfdata.log,如下子目录下的文件也读取到了
在这里插入图片描述

如下在_psurfdata()里思路是读一行,按,号拆分,将每个内容读出来再*10放入数据库
在这里插入图片描述

在这里插入图片描述

vi /htidc/shqx/ini/stcode1.ini
在这里插入图片描述

下面在_shqx.cpp中,如下是构造函数和析构函数
在这里插入图片描述

在_shqx.cpp中有InsertTable()成员函数,有select,insert,update及各个的execute()。如下如果记录在表里已存在就执行stmtupt.execute(),不存在就执行stmtins.execute(),不管是这两个执行,只要出错,无效的记录数invalidcount就+1,成功的话updatacount和insertcount都+1
在这里插入图片描述

在这里插入图片描述

如下时间和文件名一样,但站点不一样 ,59287这个站点是存在的即外键存在
在这里插入图片描述

以下是自身的调度:1.以下将psurfdata.cpp改为程序自身调度,不用crontab调度。每10秒钟扫描一次目录,有文件的话就连接数据库
在这里插入图片描述

如下为什么不在while(true)扫描目录前连上数据库?假设数据一分钟生成一个文件,处理一个文件入库一秒不到,所以一直连着数据库浪费资源,扫描到有文件处理就连数据库
在这里插入图片描述

如下数据库没连上也不用return -1,只要psurfdata.cpp启动后就一直运行,遇到错误也不能退出,所以改为break只跳出这个小循环,继续往下面执行,日志还是会写“connect database......failed”
在这里插入图片描述

2.以下crtsurfdata中自身调度
在这里插入图片描述

3.以下在ftpgetfile.cpp中自身调度
在这里插入图片描述

结构体可存不同数据类型,需解析xml,将值放入结构体再访问。start.sh里面放crt.,ftp.,p..三个启动脚本并最后加&符
在这里插入图片描述

如下是psurfdata.cpppsurfdata1.cpp_shqx.h_shqx.cpp

//将psurfdata.cpp,txt文件入库封成类
#include "_public.h"
#include "_ooci.h"
#include "_shqx.h"
CLogFile logfile;
CDir Dir;
// 处理数据文件
bool _psurfdata();
connection conn; //实例化对象,con也叫变量(称呼)
void EXIT(int sig);

int main(int argc,char *argv[])
{
  if (argc!=5)
  {
    printf("\n本程序用于处理全国气象站点观测的分钟数据,并保存到数据库的T_SURFDATA表中。\n");
    printf("这是完善后的程序,未完善的程序在psurfdata_old.cpp中。\n");
    printf("/htidc/shqx/bin/psurfdata 数据文件存放的目录 日志文件名 数据库连接参数 程序运行时间间隔\n");
    printf("例如:/htidc/shqx/bin/psurfdata /data/shqx/sdata/surfdata /log/shqx/psurfdata.log shqx/pwdidc@snorcl11g_198 10\n");
    return -1;
  }
  // 关闭全部的信号和输入输出
  CloseIOAndSignal();
  // 处理程序退出的信号
  signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
  if (logfile.Open(argv[2],"a+")==false)
  {
    printf("打开日志文件失败(%s)。\n",argv[2]); return -1;
  }
  logfile.Write("程序启动。\n");
  while (true)
  {    
////////////////////////////////////////////////1.扫描数据文件存放的目录,只匹配"SURF_ZH_*.txt"
    // logfile.Write("开始扫描目录。\n");
    if (Dir.OpenDir(argv[1],"SURF_ZH_*.txt",1000,true,true)==false)
    {
      logfile.Write("Dir.OpenDir(%s) failed.\n",argv[1]); sleep(atoi(argv[4])); continue;
    }

/////////////////////////////////////////////////2.连接数据库
    while (true)
    {
      if (Dir.ReadDir()==false) break;  
      if (conn.m_state==0)
      {
        if (conn.connecttodb(argv[3],"Simplified Chinese_China.ZHS16GBK")!=0)
        {
          logfile.Write("connect database(%s) failed.\n%s\n",argv[3],conn.m_cda.message); break;
        }
        // logfile.Write("连接数据库成功。\n");
      }  
      logfile.Write("开始处理文件%s...",Dir.m_FileName);
    
///////////////////////////////////////////////3.处理入库
      if (_psurfdata()==false) 
      {
        logfile.WriteEx("失败。\n"); break;
      }
    }
    // 断开与数据库的连接
    if (conn.m_state==1) conn.disconnect(); 
    sleep(atoi(argv[4]));
  }
  return 0;
}
void EXIT(int sig)
{
  logfile.Write("程序退出,sig=%d\n\n",sig);
  exit(0);
}
     
//////////////////////////////////////////4.处理入库
bool _psurfdata()
{
  // 打开文件
  CFile File;
  if (File.Open(Dir.m_FullFileName,"r")==false)
  {
    logfile.Write("(File.Open(%s) failed.\n",Dir.m_FullFileName); return false;
  }
  
////////////////////读取文件中的每一行记录,写入数据库的表中
  CSURFDATA SURFDATA(&conn,&logfile); 
 //上行给m_conn和m_logfile两个指针成员变量赋值初始化也可写成如下两行:
 //SURFDATA.m_conn=&conn;  //con是对象也是变量
 //SURFDATA.m_logfile=&logfile;
  char strBuffer[301];                                           
  while (true)
  {
    memset(strBuffer,0,sizeof(strBuffer));
    if (File.Fgets(strBuffer,300,true)==false) break; // 从文件中获取一行记录   
    if (SURFDATA.SplitBuffer(strBuffer)==false) { logfile.Write("%s\n",strBuffer); continue; }// 把用逗号分隔的记录拆分到结构体中
    long rc=SURFDATA.InsertTable(); // 把结构体中的数据更新到T_SURFDATA表中,因为不知道返回哪个sql,所以用long rc =
    if ( (rc>=3113) && (rc<=3115) ) return false; // 只要不是数据库session的错误,程序就继续。
    if (rc != 0) { logfile.Write("%s\n",strBuffer); continue; }
  }
  conn.commit(); // 提交事务
  File.CloseAndRemove(); // 关闭文件指针,并删除文件
  logfile.WriteEx("成功(total=%d,insert=%d,update=%d,invalid=%d)。\n",SURFDATA.totalcount,SURFDATA.insertcount,SURFDATA.updatecount,SURFDATA.invalidcount);
  return true;
}
//psurfdata1.cpp本程序只支持xml文件入库已封装成类,有keyid等字段。
//表加字段,结构体不用,在sqlplus中输入一行创建序列命令,stcode.ini不要最后留空行
#include "_public.h"
#include "_ooci.h"
#include "_shqx.h"
CLogFile logfile;
CDir Dir;
// 处理数据文件
bool _psurfdata();
connection conn;
void EXIT(int sig);

int main(int argc,char *argv[])
{
  if (argc!=5)
  {
    printf("\n本程序用于处理全国气象站点观测的分钟数据,并保存到数据库的T_SURFDATA表中。\n");
    printf("与psurfdata.cpp不同,本程序只支持xml格式。\n");
    printf("/htidc/shqx/bin/psurfdata1 数据文件存放的目录 日志文件名 数据库连接参数 程序运行时间间隔\n");
    printf("例如:/htidc/shqx/bin/psurfdata1 /data/shqx/sdata/surfdata /log/shqx/psurfdata1.log shqx/pwdidc@snorcl11g_198 10\n");
    return -1;
  }
  // 关闭全部的信号和输入输出
  CloseIOAndSignal();
  // 处理程序退出的信号
  signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
  if (logfile.Open(argv[2],"a+")==false)
  {
    printf("打开日志文件失败(%s)。\n",argv[2]); return -1;
  }
  logfile.Write("程序启动。\n");
  while (true)
  {        
//////////////////////////////////////1.扫描数据文件存放的目录,匹配"SURF_ZH_*.xml"
    // logfile.Write("开始扫描目录。\n");
    if (Dir.OpenDir(argv[1],"SURF_ZH_*.xml",1000,true,true)==false)
    {
      logfile.Write("Dir.OpenDir(%s) failed.\n",argv[1]); sleep(atoi(argv[4])); continue;
    }
    
///////////////////////////////////////2.逐个处理目录中的数据文件
    while (true)
    {
      if (Dir.ReadDir()==false) break;
          
/////////////////////////////////////////////3.连接数据库
      if (conn.m_state==0)
      {
        if (conn.connecttodb(argv[3],"Simplified Chinese_China.ZHS16GBK")!=0)
        {
          logfile.Write("connect database(%s) failed.\n%s\n",argv[3],conn.m_cda.message); break;
        }
        // logfile.Write("连接数据库成功。\n");
      }
  
      logfile.Write("开始处理文件%s...",Dir.m_FileName);
  
//////////////////////////////////////////4.处理入库
      if (_psurfdata()==false) 
      {
        logfile.WriteEx("失败。\n"); break;
      }
    }
    // 断开与数据库的连接
    if (conn.m_state==1) conn.disconnect(); 
    sleep(atoi(argv[4]));
  }
  return 0;
}
void EXIT(int sig)
{
  logfile.Write("程序退出,sig=%d\n\n",sig);
  exit(0);
}
    
//////////////////////////////////////4.处理入库
bool _psurfdata()
{
  // 打开文件
  CFile File;
  if (File.Open(Dir.m_FullFileName,"r")==false)
  {
    logfile.Write("(File.Open(%s) failed.\n",Dir.m_FullFileName); return false;
  }
  CSURFDATA SURFDATA(&conn,&logfile);
  // 读取文件中的每一行记录
  // 写入数据库的表中
  char strBuffer[301];
  while (true)
  {
    memset(strBuffer,0,sizeof(strBuffer));
    if (File.FFGETS(strBuffer,300,"<endl/>")==false) break;
    // logfile.Write("str=%s=\n",strBuffer);
    // logfile.Write("%s\n",strBuffer);    
    if (SURFDATA.SplitBuffer1(strBuffer)==false) { logfile.Write("%s\n",strBuffer); continue; }
    long rc=SURFDATA.InsertTable();
    // 只要不是数据库session的错误,程序就继续。
    if ( (rc>=3113) && (rc<=3115) ) return false;
    if (rc != 0) { logfile.Write("%s\n",strBuffer); continue; }
  }
  // 提交事务
  conn.commit();
  // 关闭文件指针,并删除文件
  File.CloseAndRemove();
  logfile.WriteEx("成功(total=%d,insert=%d,update=%d,invalid=%d)。\n",SURFDATA.totalcount,SURFDATA.insertcount,SURFDATA.updatecount,SURFDATA.invalidcount);
  return true;
}
//_shqx.h
#ifndef _SHQX_H
#define _SHQX_H
#include "_public.h"
#include "_ooci.h"
// 全国气象站点参数数据结构
struct st_stcode
{
  char provname[31];   // 省名称
  char obtid[11];      // 站点代码
  char cityname[31];   // 城市名
  double lat;          // 纬度
  double lon;          // 经度
  double height;       // 海拔高度
};
// 全国气象站点分钟观测数据结构
struct st_surfdata
{
  char obtid[11];      // 站点代码
  char ddatetime[21];  // 数据时间:格式yyyy-mm-dd hh:mi:ss。
  int  t;              // 气温:单位,0.1摄氏度
  int  p;              // 气压:0.1百帕
  int  u;              // 相对湿度,0-100之间的值。
  int  wd;             // 风向,0-360之间的值。
  int  wf;             // 风速:单位0.1m/s
  int  r;              // 降雨量:0.1mm
  int  vis;            // 能见度:0.1米
};
// 分区信号数据结构
struct st_signallog
{
  char obtid[11];
  char ddatetime[20];
  char signalname[2];
  char signalcolor[2];
};

/////////////////////////////////////////////////1.CSURFDATA类
class CSURFDATA
{
public:
  int totalcount,insertcount,updatecount,invalidcount;  // 记录总数据、插入数、更新数、无效记录数。
  struct st_surfdata m_stsurfdata;
  CSURFDATA(connection *conn,CLogFile *logfile); //在构造函数里传进参数
 ~CSURFDATA();
 
  void initdata();  // 数据初始化
  connection *m_conn; //在类里操作数据库需要一个指针,m_conn,m_logfile这两个成员需要给它们赋值
  CLogFile   *m_logfile; //在类里写日志,m_logfile->,类里不能再类实例化,所以定义为指针
 
  int iccount; //不能定义到成员函数里值会变
  sqlstatement stmtsel,stmtins,stmtupt;
  // 把用逗号分隔的记录拆分到m_stsurfdata结构中。
  bool SplitBuffer(const char *strBuffer);  
  // 把xml格式的记录拆分到m_stsurfdata结构中。
  bool SplitBuffer1(const char *strBuffer);
  // 把m_stsurfdata结构中的值更新到T_SURFDATA表中。
  long InsertTable();
};

/////////////////////////////////////////////2.CSIGNALLOG类
class CSIGNALLOG
{
public:
  int totalcount,insertcount,updatecount,invalidcount;  // 记录总数据、插入数、更新数、无效记录数。
  struct st_signallog m_stsignallog;
  vector<struct st_signallog> vsignallog;   // 容器存放一个文件的全部记录
  CSIGNALLOG(connection *conn,CLogFile *logfile);
 ~CSIGNALLOG();
 
  void initdata();  // 数据初始化
  connection *m_conn;
  CLogFile   *m_logfile;
 
  int iccount;
  sqlstatement stmtsel,stmtins,stmtupt;
  // 把记录拆分到vsignallog容器中。
  bool SplitBuffer(const char *strBuffer);
  // 把vsignallog容器中的值更新到T_SIGNALDATA表中。
  long InsertTable();
};

//////////////////////////////3.把非结构化数据文件写入oracle数据库的表中
int FileToTable(connection *in_conn,CLogFile *in_logfile,char *in_tname,char *in_filename,char *in_ddatetime);
#endif
//_shqx.cpp
#include "_shqx.h"
//////////////////////////////////////////////////1.CSURFDATA类
CSURFDATA::CSURFDATA(connection *conn,CLogFile *logfile)
{
  initdata();        // 构造函数里传入两个指针变量并赋初值
  m_conn=conn; m_logfile=logfile;  // 所以调用CSURFDATA(&conn,&logfile);就能完成初始化
}
void CSURFDATA::initdata()
{
  totalcount=insertcount=updatecount=invalidcount=0;
  m_conn=0; m_logfile=0;
  memset(&m_stsurfdata,0,sizeof(struct st_surfdata));
}
CSURFDATA::~CSURFDATA()
{
}

////////////////////////1.1 把用逗号分隔的记录拆分到m_stsurfdata结构中。
bool CSURFDATA::SplitBuffer(const char *strBuffer)
{
  totalcount++;
  memset(&m_stsurfdata,0,sizeof(struct st_surfdata));
  CCmdStr CmdStr;
  CmdStr.SplitToCmd(strBuffer,",",true);
  if (CmdStr.CmdCount()!=9) { invalidcount++; return false; }
  CmdStr.GetValue(0,m_stsurfdata.obtid,5);      // 站点代码
  CmdStr.GetValue(1,m_stsurfdata.ddatetime,19); // 数据时间:格式yyyy-mm-dd hh:mi:ss。
  double dtmp=0;
  CmdStr.GetValue(2,&dtmp); m_stsurfdata.t=(int)(dtmp*10);  // 气温:单位,0.1摄氏度
  CmdStr.GetValue(3,&dtmp); m_stsurfdata.p=(int)(dtmp*10);  // 气压:0.1百帕
  CmdStr.GetValue(4,&m_stsurfdata.u);  // 相对湿度,0-100之间的值。
  CmdStr.GetValue(5,&m_stsurfdata.wd); // 风向,0-360之间的值。
  CmdStr.GetValue(6,&dtmp); m_stsurfdata.wf=(int)(dtmp*10);  // 风速:单位0.1m/s
  CmdStr.GetValue(7,&dtmp); m_stsurfdata.r=(int)(dtmp*10);   // 降雨量:0.1mm
  CmdStr.GetValue(8,&dtmp); m_stsurfdata.vis=(int)(dtmp*10); // 能见度:0.1米
  return true;
}

///////////////////////1.2 把xml格式的记录拆分到m_stsurfdata结构中。
bool CSURFDATA::SplitBuffer1(const char *strBuffer)
{
  totalcount++;
  memset(&m_stsurfdata,0,sizeof(struct st_surfdata));
  GetXMLBuffer(strBuffer,"obtid",m_stsurfdata.obtid,5);      // 站点代码
  GetXMLBuffer(strBuffer,"ddatetime",m_stsurfdata.ddatetime,19); // 数据时间:格式yyyy-mm-dd hh:mi:ss。
  double dtmp=0;
  GetXMLBuffer(strBuffer,"t",&dtmp); m_stsurfdata.t=(int)(dtmp*10);  // 气温:单位,0.1摄氏度
  GetXMLBuffer(strBuffer,"p",&dtmp); m_stsurfdata.p=(int)(dtmp*10);  // 气压:0.1百帕
  GetXMLBuffer(strBuffer,"u",&m_stsurfdata.u);  // 相对湿度,0-100之间的值。
  GetXMLBuffer(strBuffer,"wd",&m_stsurfdata.wd);  // 风向,0-360之间的值。
  GetXMLBuffer(strBuffer,"wf",&dtmp); m_stsurfdata.wf=(int)(dtmp*10);  // 风速:单位0.1m/s
  GetXMLBuffer(strBuffer,"r",&dtmp); m_stsurfdata.r=(int)(dtmp*10);   // 降雨量:0.1mm
  GetXMLBuffer(strBuffer,"vis",&dtmp);  m_stsurfdata.vis=(int)(dtmp*10); // 能见度:0.1米
  return true;
}

//////////////////////1.3 把m_stsurfdata结构中的值更新到T_SURFDATA表中。
long CSURFDATA::InsertTable()
{
  if (stmtsel.m_state==0)
  {
    stmtsel.connect(m_conn);
    stmtsel.prepare("select count(*) from T_SURFDATA where obtid=:1 and ddatetime=to_date(:2,'yyyy-mm-dd hh24:mi:ss')");
    stmtsel.bindin( 1, m_stsurfdata.obtid,5);
    stmtsel.bindin( 2, m_stsurfdata.ddatetime,19);
    stmtsel.bindout(1,&iccount);
  }
  if (stmtins.m_state==0)
  {
    stmtins.connect(m_conn);
    stmtins.prepare("insert into T_SURFDATA(obtid,ddatetime,t,p,u,wd,wf,r,vis,crttime,keyid) values(:1,to_date(:2,'yyyy-mm-dd hh24:mi:ss'),:3,:4,:5,:6,:7,:8,:9,sysdate,SEQ_SURFDATA.nextval)");
    stmtins.bindin( 1, m_stsurfdata.obtid,5);
    stmtins.bindin( 2, m_stsurfdata.ddatetime,19);
    stmtins.bindin( 3,&m_stsurfdata.t);
    stmtins.bindin( 4,&m_stsurfdata.p);
    stmtins.bindin( 5,&m_stsurfdata.u);
    stmtins.bindin( 6,&m_stsurfdata.wd);
    stmtins.bindin( 7,&m_stsurfdata.wf);
    stmtins.bindin( 8,&m_stsurfdata.r);
    stmtins.bindin( 9,&m_stsurfdata.vis);
  }
  if (stmtupt.m_state==0)
  {
    stmtupt.connect(m_conn);
    stmtupt.prepare("update T_SURFDATA set t=:1,p=:2,u=:3,wd=:4,wf=:5,r=:6,vis=:7 where obtid=:8 and ddatetime=to_date(:2,'yyyy-mm-dd hh24:mi:ss')");
    stmtupt.bindin( 1,&m_stsurfdata.t);
    stmtupt.bindin( 2,&m_stsurfdata.p);
    stmtupt.bindin( 3,&m_stsurfdata.u);
    stmtupt.bindin( 4,&m_stsurfdata.wd);
    stmtupt.bindin( 5,&m_stsurfdata.wf);
    stmtupt.bindin( 6,&m_stsurfdata.r);
    stmtupt.bindin( 7,&m_stsurfdata.vis);
    stmtupt.bindin( 8, m_stsurfdata.obtid,5);
    stmtupt.bindin( 9, m_stsurfdata.ddatetime,19);
  }
  if (stmtsel.execute() != 0)
  {
    invalidcount++; 
    m_logfile->Write("stmtsel.execute() failed.\n%s\n%s\n",stmtsel.m_sql,stmtsel.m_cda.message); 
    return stmtsel.m_cda.rc;
  }  
  iccount=0;
  stmtsel.next();
  if (iccount>0) 
  {
    // 执行更新的SQL语句,一定要判断返回值,0-成功,其它-失败。
    if (stmtupt.execute() != 0)
    {
      invalidcount++; 
      m_logfile->Write("stmtupt.execute() failed.\n%s\n%s\n",stmtupt.m_sql,stmtupt.m_cda.message);
      return stmtupt.m_cda.rc;
    }
    updatecount++;
  }
  else
  {
    // 执行插入的SQL语句,一定要判断返回值,0-成功,其它-失败。
    if (stmtins.execute() != 0)
    {
      invalidcount++; 
      m_logfile->Write("stmtins.execute() failed.\n%s\n%s\n",stmtins.m_sql,stmtins.m_cda.message);
      return stmtins.m_cda.rc;
    }
    insertcount++;
  }
  return 0;
}

///////////////////////////////////////////////////2.CSIGNALLOG类
CSIGNALLOG::CSIGNALLOG(connection *conn,CLogFile *logfile)
{
  initdata();
  m_conn=conn; m_logfile=logfile;
}
void CSIGNALLOG::initdata()
{
  totalcount=insertcount=updatecount=invalidcount=0;
  m_conn=0; m_logfile=0;
  memset(&m_stsignallog,0,sizeof(struct st_signallog));
  vsignallog.clear();
}
CSIGNALLOG::~CSIGNALLOG()
{
}

////////////////////////////2.1 把记录拆分到vsignallog容器中。
bool CSIGNALLOG::SplitBuffer(const char *strBuffer)
{
  vsignallog.clear();
  memset(&m_stsignallog,0,sizeof(struct st_signallog));
  
  CCmdStr CmdStr;  
  CmdStr.SplitToCmd(strBuffer," ",true);
  if (CmdStr.CmdCount()<3) { invalidcount++; return false; }
  CmdStr.GetValue(0,m_stsignallog.ddatetime,12); // 数据时间:格式yyyymmddhh24mi
  strcat(m_stsignallog.ddatetime,"00");
  AddTime(m_stsignallog.ddatetime,m_stsignallog.ddatetime,8*60*60,"yyyy-mm-dd hh24:mi:ss");
 
  CmdStr.GetValue(1,m_stsignallog.obtid,4);      // 站点代码
  char strtemp[11];
  for (int ii=3;ii<=CmdStr.CmdCount();ii++)
  { // 201809142353 GWTE A3000 ....= 
    memset(strtemp,0,sizeof(strtemp));
    CmdStr.GetValue(ii-1,strtemp,5);  // m_stsignallog.signalname[0]表示字符串中第一个字符
    m_stsignallog.signalname[0]=strtemp[0]; //strtemp[0]就是A
    m_stsignallog.signalcolor[0]=strtemp[1]; //strtemp[1]就是3
    vsignallog.push_back(m_stsignallog);
    totalcount++;
  }
  return true;
}

////////////////////2.2 把vsignallog容器中的值更新到T_SIGNALDATA表中
long CSIGNALLOG::InsertTable() //可能会返回stmtupt.m_cda.rc,所以用long
{
  if (stmtsel.m_state==0)
  {
    stmtsel.connect(m_conn);
    // 如下这个表的主键有三个字段
    stmtsel.prepare("select count(*) from T_SIGNALLOG where obtid=:1 and ddatetime=to_date(:2,'yyyy-mm-dd hh24:mi:ss') and signalname=:3");
    stmtsel.bindin( 1, m_stsignallog.obtid,4);
    stmtsel.bindin( 2, m_stsignallog.ddatetime,19);
    stmtsel.bindin( 3, m_stsignallog.signalname,1);
    stmtsel.bindout(1,&iccount);
  }
  if (stmtins.m_state==0)
  {
    stmtins.connect(m_conn);
    stmtins.prepare("insert into T_SIGNALLOG(obtid,ddatetime,signalname,signalcolor,crttime,keyid) values(:1,to_date(:2,'yyyy-mm-dd hh24:mi:ss'),:3,:4,sysdate,SEQ_SIGNALLOG.nextval)");
    stmtins.bindin( 1, m_stsignallog.obtid,4);
    stmtins.bindin( 2, m_stsignallog.ddatetime,19);
    stmtins.bindin( 3, m_stsignallog.signalname,1);
    stmtins.bindin( 4, m_stsignallog.signalcolor,1);
  }
  if (stmtupt.m_state==0)
  {
    stmtupt.connect(m_conn);
    stmtupt.prepare("update T_SIGNALLOG set signalcolor=:1 where obtid=:2 and ddatetime=to_date(:3,'yyyy-mm-dd hh24:mi:ss') and signalname=:4");
    stmtupt.bindin( 1, m_stsignallog.signalcolor,1);
    stmtupt.bindin( 2, m_stsignallog.obtid,4);
    stmtupt.bindin( 3, m_stsignallog.ddatetime,19);
    stmtupt.bindin( 4, m_stsignallog.signalname,1);
  }
  for (int ii=0;ii<vsignallog.size();ii++)
  { //把容器里的值拷出来
    memcpy(&m_stsignallog,&vsignallog[ii],sizeof(struct st_signallog));
    m_logfile->Write("%s,%s,%s,%s\n",m_stsignallog.obtid,m_stsignallog.ddatetime,m_stsignallog.signalname,m_stsignallog.signalcolor);
    if (stmtsel.execute() != 0)
    {
      invalidcount++; 
      m_logfile->Write("stmtsel.execute() failed.\n%s\n%s\n",stmtsel.m_sql,stmtsel.m_cda.message); 
      return stmtsel.m_cda.rc;
    }  
    iccount=0;
    stmtsel.next();  
    if (iccount>0) 
    {
      // 执行更新的SQL语句,一定要判断返回值,0-成功,其它-失败。
      if (stmtupt.execute() != 0)
      {
        invalidcount++; 
        m_logfile->Write("stmtupt.execute() failed.\n%s\n%s\n",stmtupt.m_sql,stmtupt.m_cda.message);
        return stmtupt.m_cda.rc;
      }
      updatecount++;
    }
    else
    {
      // 执行插入的SQL语句,一定要判断返回值,0-成功,其它-失败。
      if (stmtins.execute() != 0)
      {
        invalidcount++; 
        m_logfile->Write("stmtins.execute() failed.\n%s\n%s\n",stmtins.m_sql,stmtins.m_cda.message);
        return stmtins.m_cda.rc;
      }
      insertcount++;
    }
  }
  return 0;
}

////////////////////////////3.把非结构化数据文件写入oracle数据库的表中
int FileToTable(connection *in_conn,CLogFile *in_logfile,char *in_tname,char *in_filename,char *in_ddatetime)
{
  sqlstatement stmt(in_conn);
  // 判断文件记录在表中是否已存在
  int icount=0;
  stmt.prepare("select count(*) from %s where filename=:1",in_tname);
  stmt.bindin(1,in_filename,300);
  stmt.bindout(1,&icount);
  if (stmt.execute() != 0)
  {
    in_logfile->Write("FileToTable() failed.%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return stmt.m_cda.rc;
  }
  stmt.next();
  // 如果记录已存在,直接返回0-成功。
  if (icount>0) return 0;
  // 把文件信息插入表中。
  int ifilesize=FileSize(in_filename);
  stmt.prepare("\
       insert into %s(filename,ddatetime,filesize,filecontent,crttime,keyid)\
               values(:1,to_date(:2,'yyyymmddhh24miss'),:3,empty_blob(),sysdate,SEQ_%s.nextval)",\
       in_tname,in_tname+2);  
  stmt.bindin(1,in_filename,300); //empty_blob()可以换成null试试,文件内容可以不弄到blob字段
  stmt.bindin(2,in_ddatetime,14);
  stmt.bindin(3,&ifilesize);  
  if (stmt.execute() != 0)
  {
    in_logfile->Write("FileToTable() failed.%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return stmt.m_cda.rc;
  }    
  // 把文件内容更新到BLOB字段中。
  stmt.prepare("select filecontent from %s where filename=:1 for update",in_tname);
  stmt.bindin(1,in_filename,300);
  stmt.bindblob();
  // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
  if (stmt.execute() != 0)
  {
    in_logfile->Write("FileToTable() failed.%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return stmt.m_cda.rc;
  }
  // 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。
  if (stmt.next() != 0) return -1;
  // 把磁盘文件pic_in.jpg的内容写入BLOB字段,一定要判断返回值,0-成功,其它-失败。
  if (stmt.filetolob((char *)in_filename) != 0)
  {
    in_logfile->Write("FileToTable() stmt.filetolob() failed.\n%s\n",stmt.m_cda.message); return -1;
  }
  // 提交事务
  in_conn->commit(); //图片数据大,一个文件提交一次
  return 0;
}

2.crontab缺环境变量/分区/表空间/网络/主键/错误优化:su,creuser.sql

用crontab调度是没有shell环境,程序运行需要数据库环境,相当于下图(su - user获取user环境变量),所以不能用crontab。


在这里插入图片描述

下面输出环境变量写入日志文件中,logfile先Open再Write


在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

下面为脚本输出环境变量,然后再执行程序,这种方法可行


在这里插入图片描述

在这里插入图片描述

如下用crontab调用psurfdata.sh同上面一样都可行
在这里插入图片描述

在这里插入图片描述

下面是虚拟机只有一个分区,其他都是临时文件系统
在这里插入图片描述

下面是实际安装的linux系统
在这里插入图片描述

用文件夹代替实际分区。root用户mkdir htidc log data,htidc分public和shqx两个文件夹。如下授权给Oracle用户


在这里插入图片描述

下面为改进makefile,PUBINCL中-I指定头文件_public.h,_ftp.h等等搜索目录,自己去找头文件。冒号前后整个一行等于下行编译命令。冒号后面文件有更新(必须要有一个同名cpp文件),则重新编译(只是有这个重编译功能),一般后面不放_ooci...因为稳定不需要改动
# oracle头文件路径
ORAINCL = -I$(ORACLE_HOME)/rdbms/public
# oracle库文件路径
ORALIB =  -L$(ORACLE_HOME)/lib -L.
# oracle的oci库
ORALIBS = -lclntsh

# 通用框架头文件路径
PUBINCL = -I/htidc/public/c
# 通用框架库文件路径
PUBCPP = /htidc/public/c/_public.cpp 
FTPCPP = /htidc/public/c/_ftp.cpp 
OCICPP = /htidc/public/c/_ooci.cpp

CC=g++
# CFLAGS = -O2
# CFLAGS = -O2 -Wall
CFLAGS = -g -Wno-write-strings -Wno-unused-variable

all: crtsurfdata psurfdata psignallog wgetrain24\
     wgettemp24 pzhrain24file

crtsurfdata:crtsurfdata.cpp _shqx.h _shqx.cpp
    $(CC) $(CFLAGS) -o crtsurfdata crtsurfdata.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) $(PUBINCL) $(PUBCPP) $(OCICPP) _shqx.cpp -lm -lc
    cp -f crtsurfdata ../bin/.

psurfdata: psurfdata.cpp _shqx.h _shqx.cpp
    $(CC) $(CFLAGS) -o psurfdata psurfdata.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) $(PUBINCL) $(PUBCPP) $(OCICPP) _shqx.cpp -lm -lc
    cp -f psurfdata ../bin/.

pzhrain24file: pzhrain24file.cpp _shqx.h _shqx.cpp
    $(CC) $(CFLAGS) -o pzhrain24file pzhrain24file.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) $(PUBINCL) $(PUBCPP) $(OCICPP) _shqx.cpp -lm -lc
    cp -f pzhrain24file ../bin/.

psignallog: psignallog.cpp _shqx.h _shqx.cpp
    $(CC) $(CFLAGS) -o psignallog psignallog.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) $(PUBINCL) $(PUBCPP) $(OCICPP) _shqx.cpp -lm -lc
    cp -f psignallog ../bin/.

wgetrain24: wgetrain24.cpp 
    $(CC) $(CFLAGS) -o wgetrain24 wgetrain24.cpp $(PUBINCL) $(PUBCPP) -lm -lc
    cp -f wgetrain24 ../bin/.

wgettemp24: wgetrain24.cpp 
    $(CC) $(CFLAGS) -o wgettemp24 wgettemp24.cpp $(PUBINCL) $(PUBCPP) -lm -lc
    cp -f wgettemp24 ../bin/.

clean:
    rm -rf crtsurfdata psurfdata psignallog wgetrain24
    rm -rf wgettemp24 pzhrain24file

切换用户如下目录里的*.sh都会执行,和oracle名字无关。如上makefile中不需要数据库就不要包含路径

在这里插入图片描述

在这里插入图片描述

如下crttable.sql里最后不写exit;的话下行登录sqlplus不退出,每行sql语句需要分号结尾。如下.sql文件里一般放一些系统初始化sql语句如建表,839行insert站点参数。snorcl11g_198是连远程该服务名指定的数据库,不加就连本地数据库
在这里插入图片描述

下面是数据库设计,每个项目新建一个数据库用户,所有对象都会存在用户里。oracle不同于其他数据库,存储结构中没有数据库这个概念,表、视图等对象属于用户。oracle安装好后会自动创建几个表空间,表空间是用来存放数据库对象的一个空间,类似分区,如下是查看oracle表空间大小的sql(登录再执行)

SELECT
a.tablespace_name, --表空间名
total, --表空间大小
free, --表空间剩余大小
(total-free), --表空间使用大小
Round((total-free)/total,4)*100 --使用率
FROM (SELECT tablespace_name,Sum(bytes) free
FROM DBA_FREE_SPACE
GROUP BY tablespace_name) a,
(SELECT tablespace_name,Sum(bytes) total
FROM DBA_DATA_FILES
GROUP BY tablespace_name)b
WHERE
a.tablespace_name=b.tablespace_name;

SYSTEM表空间(创建数据库时创建的)相当于win系统C盘,USERS表空间相当于D盘放数据,UNDOTBS1表空间相当于事务缓冲区,SYSAUX相当于SYSTEM表空间辅助分区

在这里插入图片描述

创建新用户并指定该用户缺省表空间为users,vi creuser.sql

--把数据库用户允许错误重试的次数改为不限制
alter profile DEFAULT limit FAILED_LOGIN_ATTEMPTS UNLIMITED;
alter profile DEFAULT limit PASSWORD_LIFE_TIME  UNLIMITED;

-- 上海气象数据中心的主用户,shqx为用户名,pwdidc为密码,default tablespace users默认user表空间
-- drop user shqx cascade;
create user shqx profile default identified by pwdidc default tablespace users account unlock;
grant connect to shqx;
grant dba to shqx;

exit;

下面是以超级用户登录,/指不用任何密码,不能远程登录只能登录本机且装有数据库

在这里插入图片描述

如下是网络/主键/错误优化 :入库时查看日志,时间太长,truncate删除表记录继续入库时间测试
在这里插入图片描述

如上连本机数据库1秒之内处理完,下面方式和不带服务名一样也不通过网络设备,日志都为1秒左右
在这里插入图片描述

如下对两个网卡插上交换线就不需要通过交换机
在这里插入图片描述

在这里插入图片描述

以上说明第一点:网络对性能影响大,下面是第二点:主键冲突(数据库已存在主键即有记录了,没有truncate)
在这里插入图片描述

在这里插入图片描述

下面为改善:先去表中查下有没有这条记录存在,有的话就不插入insert入库,m_cda.rc报个错,这样能提高速度解决主键冲突。以下在psurfdata.cpp中,2个主键查询。bindin行,bindout列。
在这里插入图片描述

如下出现rc=1重复记录即主键冲突就不管,不等于1日志写错误信息并直接return false
在这里插入图片描述

如上行出现问题logfile.write("stmtins.execute() fail.........最后直接return false(-1)退出整个大循环,如下所示
在这里插入图片描述

所以将return false改为continue(这个小循环下面不执行,重新从这个小循环开头执行),break(跳出小循环往后执行,不重小循环开头开始)
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

如下oracle类似的错误代码是连在一起的,以下两个错必须退出(return false),其他的错没必要退出
在这里插入图片描述

在这里插入图片描述

3.历史文件/数据清理/备份/资源释放:start.sh,tar

下面为历史文件的清理,生成测试数据后去采集。采集后未删除文件,因为数据不只一个系统用。一般两个需求:删除哪个时间点前文件(常用)只保留多少个文件(一般删除数据库归档日志)
1.以下是删除多长时间之前文件(生成或采集的.txt数据文件)

//这个程序不写日志,deletefiles.cpp
#include "_public.h"
void EXIT(int sig);
// 显示程序的帮助,argv[]未指定字符串长度
void _help(char *argv[]); 

int main(int argc,char *argv[])
{ //最后一个参数可有可无,如下argv是变量相当于&argv或&argv[0]
  if ( (argc != 3) && (argc != 4) ) { _help(argv); return -1; } 
  // 关闭全部的信号和输入输出,可以先//了,因为调试时要输出信息
  //CloseIOAndSignal();
  // 处理程序退出的信号设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程,但请不要用 "kill -9 +进程号" 强行终止
  signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
  
  //strPathName:/data/shqx/ftp/surfdata,dDayOut,strTimeOut
  char   strPathName[201];
  double dDayOut=0;
  memset(strPathName,0,sizeof(strPathName));
  strcpy(strPathName,argv[1]);  
  dDayOut=atof(argv[2]);    
  char strTimeOut[21];
  LocalTime(strTimeOut,"yyyy-mm-dd hh24:mi:ss",0-(int)(dDayOut*24*60*60)); //获取需要删的时间点存入strTimeOut中
  
  //strMatch:\"*.TXT,*.CSV\"
  CDir Dir;
  char strMatch[50]; memset(strMatch,0,sizeof(strMatch));
  if (argc==3) strcpy(strMatch,"*"); //如果只有3个参数,全删
  else strcpy(strMatch,argv[3]);  

//////////////////////////////////////////////////1.打开目录,读取文件,包括它的子目录
  if (Dir.OpenDir(strPathName,strMatch,10000,true,false) == false)
  {
    printf("Dir.OpenDir(%s) failed.\n",strPathName); return -1;
  }
  char strLocalTime[21];
  while (Dir.ReadDir() == true)
  {
    if (strcmp(Dir.m_ModifyTime,strTimeOut) > 0) continue; //m_ModifyTime大于strTimeOut的话就不用删除
    printf("delete %s ok.\n",Dir.m_FullFileName);
    REMOVE(Dir.m_FullFileName); //C语言提供了remove函数,在_public.cpp中重写了REMOVE,删不了多试几次
  }
  return 0;
}
void EXIT(int sig)
{
  printf("程序退出,sig=%d\n\n",sig);
  exit(0);
}

//////////////////////////////////////2.显示程序的帮助
void _help(char *argv[])
{
  printf("\n");
  printf("Using:/htidc/public/bin/deletefiles pathname dayout [matchstr]\n");
  printf("Sample:/htidc/public/bin/deletefiles /data/shqx/ftp/surfdata 0.1 \"*.TXT,*.CSV\"\n\n");
  // dayout单位是天,如果要清除30分钟前数据,则dayout填30/(24*60),如上0.1*24*60=144分钟,就是当前时间往前推144分钟
  printf("本程序是数据中心的公共功能模块,用于删除指定目录下的历史文件。\n");
  printf("pathname 待清理的目录名,包括这个目录下的各级子目录。\n");
  printf("dayout   文件保留天数,单位是天,可以用小数。\n");
  printf("matchstr 待清理文件名的匹配规则,这是一个可选参数,可以匹配多种类型的文件,中间用逗号分隔,最好用双引号包含起来。\n\n");
}

deletefiles.cpp不用自身调度用crontab -e,最后一个参数可不写,写了必须加双引号

在这里插入图片描述

在这里插入图片描述

下面改进,写日志后清空两个容器,在crtsurfdata.cpp中
在这里插入图片描述

在这里插入图片描述

2.下面是第二个需求,保留多少文件

//删除数据库的归档日志,deletearchive.cpp
#include "_public.h"
int main(int argc,char *argv[])
{
  char strPathName[201];
  int uSaveCount=0;
  if (argc != 3)
  {
    printf("\n");
    printf("Using:/htidc/htidc/bin/deletearchive pathname savecount\n\n");
    printf("Example:/htidc/htidc/bin/procctl 300 /htidc/htidc/bin/deletearchive /oracle/archive 20\n\n");
    printf("本程序读取目定pathname录下的文件信息,并按时间降序,只保留最近savecount个文件,其它的都删除掉。\n");
    printf("本程序主要用于删除oracle数据库的归档日志文件。\n");
    printf("本程序不写日志文件,也不会在屏幕上输出任何信息。\n");
    printf("本程序调用/bin/ls -lt pathname获取归档日志文件信息。\n");
    printf("本程序可以手工运行,也可以由procctl调度。\n\n\n");
    printf("启用oracle归档日志的相关命令如下:\n");
    printf("sqlplus /nolog\n");
    printf("connect / as sysdba;\n");
    printf("alter system set log_archive_dest_1='location=/home/oracle/oradata/EJETDB/archive';\n");
    printf("shutdown immediate;\n");
    printf("startup mount;\n");
    printf("alter database archivelog;\n");
    printf("alter database open;\n");
    printf("alter system switch logfile;\n\n\n");
    return -1;
  }
  memset(strPathName,0,sizeof(strPathName));
  strcpy(strPathName,argv[1]);
  uSaveCount=atoi(argv[2]);
  // 关闭全部的信号和输入输出
  // 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程
  // 但请不要用 "kill -9 +进程号" 强行终止
  CloseIOAndSignal(); 
  
  //strPathName:/oracle/archive
  FILE *fp=NULL;
  char strCmd[301];
  memset(strCmd,0,sizeof(strCmd)); 
  sprintf(strCmd,"/bin/ls -lt %s",strPathName);  
  if ( (fp=popen(strCmd,"r")) == NULL )
  {
    printf("popen %s failed.\n",strCmd); return -1;
  }
  
  //fp指向ls -l的输出的内容
  CCmdStr CmdStr;
  int uFetchedCount=0;
  char strBuffer[1024],strFullFileName[201];
  while (TRUE)
  {
    memset(strBuffer,0,sizeof(strBuffer));
    if (FGETS(strBuffer,2000,fp) == FALSE) break;  
    uFetchedCount++;
    if (uFetchedCount <= uSaveCount + 1 ) continue;
    
    // Trim(strBuffer);
    UpdateStr(strBuffer,"  "," "); 
    CmdStr.SplitToCmd(strBuffer," ");
    memset(strBuffer,0,sizeof(strBuffer));
    
    CmdStr.GetValue(CmdStr.CmdCount()-1,strBuffer,200);
    memset(strFullFileName,0,sizeof(strFullFileName));
    snprintf(strFullFileName,200,"%s/%s",strPathName,strBuffer);
    REMOVE(strFullFileName);
  }
  pclose(fp);
  return 0;
}

3.下面为工具程序用于清理表中数据

//deletetables.cpp
#include "_public.h"
#include "_ooci.h"
char logfilename[301];
char connstr[101];
char tname[51];
char where[1024];
char hourstr[101];
char localhour[21];
connection conn;
CLogFile logfile;
void EXIT(int sig);
// 显示程序的帮助
void _help(char *argv[]);
// 每批删除的记录数
int  maxcounts=500;
bool _deletetables();

int main(int argc,char *argv[])
{
  if (argc != 2) { _help(argv); return -1; }
  memset(logfilename,0,sizeof(logfilename));
  memset(connstr,0,sizeof(connstr));
  memset(tname,0,sizeof(tname));
  memset(where,0,sizeof(where));
  memset(hourstr,0,sizeof(hourstr));
  GetXMLBuffer(argv[1],"logfilename",logfilename,300);
  GetXMLBuffer(argv[1],"connstr",connstr,100);
  GetXMLBuffer(argv[1],"tname",tname,50);
  GetXMLBuffer(argv[1],"where",where,1000);
  GetXMLBuffer(argv[1],"hourstr",hourstr,2000);
  //上面为获取参数放入一开始定义的几个char字符串中,下面是判断参数的合法性
  if (strlen(logfilename) == 0) { printf("logfilename is null.\n"); return -1; }
  if (strlen(connstr) == 0)     { printf("connstr is null.\n"); return -1; }
  if (strlen(tname) == 0)       { printf("tname is null.\n"); return -1; }
  if (strlen(where) == 1)       { printf("where is null.\n"); return -1; }
  if (strlen(hourstr) == 0)     { printf("hourstr is null.\n"); return -1; }
  CloseIOAndSignal();//关闭全部的信号和输入输出
  signal(SIGINT,EXIT); signal(SIGTERM,EXIT);//处理程序退出的信号
  // 打开日志文件
  if (logfile.Open(logfilename,"a+") == FALSE)
  {
    printf("logfile.Open(%s) failed.\n",logfilename); return -1;
  }
  while (true) //自身调度,不用crontab
  {
    // 判断当前时间是否在启动时间之内
    memset(localhour,0,sizeof(localhour));
    LocalTime(localhour,"hh24");
    if (strstr(hourstr,localhour)==0) { sleep(60); continue; }
    // 连接数据库
    if (conn.connecttodb(connstr,"Simplified Chinese_China.ZHS16GBK") != 0)
    {
      logfile.Write("connect database %s failed.\n",connstr); sleep(60); continue; 
    }
    logfile.Write("delete table %s.\n",tname);
    if (_deletetables() == false) logfile.Write("deletetables failed.\n"); 
    conn.disconnect();
    sleep(60); 
  }
  return 0;
}
void EXIT(int sig)
{
  printf("程序退出,sig=%d\n\n",sig);
  exit(0);
}
// 显示程序的帮助
void _help(char *argv[])
{
  printf("\nUsing:/oracle/htidc/shqx/bin/deletetables \"<logfilename>/oracle/log/shqx/deletetables_SURFDATA.log</logfilename><connstr>shqx/pwdidc@snorcl11g_188</connstr><tname>T_SURFDATA</tname><where>where ddatetime<sysdate</where><hourstr>14,15,16</hourstr>\"\n\n");

  printf("这是一个工具程序,用于清理表中的数据。\n");
  printf("<logfilename>/log/shqx/deletetables_ALLAWSDATA.log</logfilename> 本程序运行日志文件名。\n");
  printf("<connstr>szidc/pwdidc@SZQX_10.153.97.251</connstr> 目的数据库的连接参数。\n");
  printf("<tname>T_ALLAWSDATA</tname> 待清理的表名。\n");
  printf("<where>where ddatetime<sysdate-5</where> 待清理数据的条件。\n");
  printf("<hourstr>01,02,03</hourstr> 本程序启动的时次,小时,时次之间用半角的逗号分隔开。\n\n");
  return;
}

///////////////////////////////////////////////
bool _deletetables()
{
  int  ccount=0;
  char strrowid[51],strrowidn[maxcounts][51];
  //sqlstatement stmt;
  //stmt.prepare("delete from %s %s",tname,where);
  //stmt.execute();
  //conn.commit();
  //如上这么写不行,如果表要删除的数据很多,delete from....这语句跑不动,不能让Oracle产生大事务
  //stmt.prepare("delete from %s %s" and rownum<10000",tname,where);
  //如上一次删除10000条,这种方法效率不高,假设where这个条件带的参数查这个数据时,假设这字段没有索引,那这sql执行时间相当长
  
///////////////////////////////////////////////1.获取符合条件的记录的rowid
  sqlstatement selstmt(&conn);
  selstmt.prepare("select rowid from %s %s",tname,where);
  selstmt.bindout(1, strrowid,50);
  if (selstmt.execute() != 0)
  {
    logfile.Write("%s failed.\n%s\n",selstmt.m_sql,selstmt.m_cda.message); return false;
  }

///////////////////////////////////////2.生成删除数据的SQL语句,一次删除maxcounts条记录。
  int ii=0;
  char strDeleteSQL[10241];
  memset(strDeleteSQL,0,sizeof(strDeleteSQL));
  sprintf(strDeleteSQL,"delete from %s where rowid in (",tname);
  char strtemp[11];
  for (ii=0; ii<maxcounts; ii++)
  {
    memset(strtemp,0,sizeof(strtemp));
    if (ii==0) sprintf(strtemp,":%d",ii+1); //:号是sql绑定变量
    if (ii >0) sprintf(strtemp,",:%d",ii+1);
    strcat(strDeleteSQL,strtemp);
  }
  strcat(strDeleteSQL,")");
  sqlstatement delstmt(&conn);
  delstmt.prepare(strDeleteSQL);
  for (ii=0; ii<maxcounts; ii++)
  {
    delstmt.bindin(ii+1,strrowidn[ii],50);
  }
  while (true)
  {
    memset(strrowid,0,sizeof(strrowid));
    if (selstmt.next() != 0) break;
    strcpy(strrowidn[ccount],strrowid);
    ccount++;
//logfile.Write("%s\n",delstmt.m_sql);
    if (ccount == maxcounts)
    {
      if (delstmt.execute() != 0)
      {
        logfile.Write("delete %s failed.\n%s\n",tname,delstmt.m_cda.message); return false;
      }
      conn.commit();
      memset(strrowidn,0,sizeof(strrowidn));
      ccount=0;
    }

//////////////////////////////////3.删除的记录数到10000时提交一次事务,不让数据库产生大事务
    if (fmod(selstmt.m_cda.rpc,10000) < 1)
    {
      logfile.Write("rows %d deleted.\n",selstmt.m_cda.rpc);

      //表里数据可能会非常多,几个小时都删不完,只要不在启动时间内就不干了,明天再说
      //干完活了,删除完记录了再判断当前时间是否在启动时间之内 
      memset(localhour,0,sizeof(localhour));
      LocalTime(localhour,"hh24");
      if (strstr(hourstr,localhour)==0) return true;
    }
  }

////////////////////////////////////4.在以上循环处理的时候,如果不足maxcounts,就在这里处理
  for (ii=0; ii<ccount; ii++)
  {
    delstmt.prepare("delete from %s where rowid=:1",tname);//剩下的一次删一条
    delstmt.bindin(1,strrowidn[ii],50);
//logfile.Write("%s\n",delstmt.m_sql);
    if (delstmt.execute() != 0)
    {
      if (delstmt.m_cda.rc != 1)
      {
        logfile.Write("delete %s failed.\n%s\n",tname,delstmt.m_cda.message); return false;
      }
    }
  }
  logfile.Write("rows %d deleted.completed.\n",selstmt.m_cda.rpc);
  conn.commit();
  return true;
}

如下0.7天前就是0.7*24=16.8,即16.8个小时前(1小时前大约0.04天前)


在这里插入图片描述

vi /log/shqx/deletetables_........log(如上文件名)如下


在这里插入图片描述

执行一条sql语句就会删除500条,最后不到500,一条一条删日志如下
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

下面为备份和资源释放,tar第三个参数是需要被打包的文件夹


在这里插入图片描述

退出程序方法有两种:return和exit。下面测试return时Clogfile类中析构函数有没有被调用(资源释放)
在这里插入图片描述

在这里插入图片描述

上面得出结论,不管资源定义在哪,return都会释放资源。下面测试exit
在这里插入图片描述

在这里插入图片描述

所以这些类的资源需要定义为全局变量。若定义为main函数的局部对象,当用ctrl+c和kill信号调用EXIT时不会释放资源
在这里插入图片描述

4.站点参数建表入表/PowerDesigner/主外键约束视图:分表,union

vi crttable.sql,T_OBTCODE这个表没必要创建索引,因为数据量很少

drop table T_OBTCODE;
create table T_OBTCODE
(
  obtid      char(5),
  cityname   varchar2(30),
  provname   varchar2(30),
  lat        number(5,2),
  lon        number(5,2),
  height     number(8,2),
  rsts       number(1),    --状态:1-启用,2-禁用,3-故障
  primary key(obtid)
);

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

如下图整列(列操作)插入',1);,技巧:alt+shift+鼠标拖动右边滚条到最低+鼠标点击最低。如下取消勾选全词匹配。
在这里插入图片描述

在这里插入图片描述

vi T_OBTCODE.sql将上面列操作实现的sql全部复制进去
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

vi T_OBTCODE.sql,改完后
在这里插入图片描述

PowerDesigner安装链接:https://pan.baidu.com/s/1TR5tT6qh7G4CVPDFxZ7_wg 提取码:vx9m
在这里插入图片描述

将上面汉化..文件夹里文件全复制替换到下面安装目录中(可改变安装目录)
在这里插入图片描述

上海气象.pdm文件:链接:https://pan.baidu.com/s/1zkzGDQuggwZhd1oNobeVqQ 提取码:0ad6
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

下面为保存并生成.sql文件
在这里插入图片描述

在这里插入图片描述

如下还要将双引号替换为空,不同数据库生成不同sql语句,如下修改设置就不生成双引号了
在这里插入图片描述

1.下面为主键命名,主键在Columns里p字段已指定
在这里插入图片描述

如下是主键的另一种写法
在这里插入图片描述

2.下面为创建索引
在这里插入图片描述

在这里插入图片描述

3.下面为创建表空间,如下将表存入名为USERS的表空间
在这里插入图片描述

如下将索引存入名为INDEXS表空间
在这里插入图片描述

如下主键就是索引也有这些参数也扔入INDEXS表空间
在这里插入图片描述

在这里插入图片描述

4.字段值附上约束条件,如果复制其他表记得删除字段值约束条件
在这里插入图片描述

如下U是unique index,记录序号即keyid是唯一约束,第三行是数据时间
在这里插入图片描述

下面是创建序列
在这里插入图片描述

ctrl+G生成.sql文件,注意主键名不要重复,双引号替换为空。复制刚生成的.sql文件内容到crttable.sql中,执行如下命令必须其他窗口SQL>exit
在这里插入图片描述

1.创建主外键约束。链表用容器替换,排序:C/C++sort函数,容器也有排序,数据库orderby
在这里插入图片描述

在这里插入图片描述

站点代码在如下左表里是主键,在右表是外键,命名为:FK_子表_父表。在SURFDATA(右)表上建立了外键约束,如下图标是创建主外键连接线。如下<pk,fk>,pk主键勾上p字段,fk外键勾上f字段
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.下面演示出错情况,建立这种约束后有什么效果,双击主外键约束线
在这里插入图片描述

ctrl+G生成crebas.sql文件,复制到crttable.sql
在这里插入图片描述

:g/"/s// /g
在这里插入图片描述

3.如果表被锁住(未提交)资源忙超时,dbshut关数据库再开。左边updata只有none和restrict两个选项。如下两个都为restrict为父表删不了子表已经在用的记录
在这里插入图片描述

在这里插入图片描述

将上面复制修改到下面crttable.sql中,之前有生成脚本写入过,这里只要修改下最后就行
在这里插入图片描述

如下删除59493等等其他的可以删除,但59287不行,因为已经在查询中用了,所以如下为上面两个Restrict效果
在这里插入图片描述

4.父表删一条记录,子表和它相关的记录就被删
在这里插入图片描述

preview将引号换为空格,复制粘贴到SQL>(先登录SQL),不用重新建表
在这里插入图片描述

5.set null前要将子表的站点代码字段只设置为fk,pk即p字段勾选取消
在这里插入图片描述

如上preview将引号换为空格,复制粘贴到SQL>(先登录SQL),不用重新建表
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

如上两个表一般不会创建外键约束,测试数据表非常大且对其操作非常多,建立约束往测试数据表插入数据时数据库肯定到站点表里检查约束有开销效率低。如下勾上主键再删除外键线才会保留列
在这里插入图片描述

如下几个表数据量不大,操作员基本信息表相当于qq号,地址簿群组相当于qq好友分组,要建立约束
在这里插入图片描述

在这里插入图片描述

如下是多表查询,想用一个sql语句查出数据同时也将站点名称obtname也查出来
在这里插入图片描述

6.下面为视图(安全性),create...as..上行sql
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345