@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.cpp
,psurfdata1.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