文章目录
- 1.oracle安装:swap,dd
- 1.1 创建swap交换区:grep MemTotal /proc/meminfo (安装Oracle物理内存要求1024MB以上),grep SwapTotal /proc/meminfo
- 1.2 安装依赖包及改系统核心参数:关闭一些系统对数据库的限制,pam.d,inittab
- 1.3 数据库的启动和关闭:oracle用户登录,lsnrctl start/stop/status 启动/关闭网络监听服务,dbstart/dbshut 启动/关闭数据库
- 1.4 sqlplus命令行登录数据库:用oracle用户登录,执行sqlplus scott/tiger,以scott用户的身份登录
- 1.5 plsql客户端登录数据库:远程连接需打开监听lsnrctl start
- 2.C语言操作oracle:trim,rc,库/头文件
- 2.1 makefile:-Wno-unused-variable变量未使用关闭警告(如int cc;下面未使用cc变量)
- 2.2 createtable.cpp:setenv/putenv设置环境变量,char补空格,varchar不补空格。确定长度用char快,varchar2(4000)用到几个字符自动分配几个而不是一下给4000字符
- 2.3 inserttable.cpp:truncate删除表中全部数据,不产生事务
- 2.4 selecttable.cpp:stmt.m_cda.rpc变量保存了SQL执行后影响的记录数
- 2.5 updatetable.cpp:连接数据库,返回值0成功,其它失败,失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中
- 2.6 deletetable.cpp:删除商品表中数据
- 2.7 execplsql.cpp:执行一个PL/SQL过程
- 3.虚表/日期/序列:ntp&hwclock
- 4.索引/视图/链路/同义词:create index/view/link/synonym,distinct
- 5.系统高可用性:12个1T组合,rac
- 6.txt/xml文件入表:结构体内容
- 7.站点参数建表入表/PowerDesigner/主外键:多表查询
- 8.数据交换:数据导出为文件,数据文件推送
- 9.非结构化数据存储:blob,pzhrain24file
- 10.磁盘/cpu信息收集:内存信息free -m,和top命令查看的内存是一样的,也在系统文件/proc/meminfo
1.oracle安装:swap,dd
1.1 创建swap交换区:grep MemTotal /proc/meminfo (安装Oracle物理内存要求1024MB以上),grep SwapTotal /proc/meminfo
下面为添加交换区大小,此方法不限于centos,linux均适用,以下命令均需在root帐号下操作:
(1)
先用free -m查看一下swap的大小。
(2)
使用dd命令创建/home/swap这么一个分区文件。
(3)
接着再把这个分区变成swap分区。
(4)
再接着使用这个swap分区。使其成为有效状态。
(5)
现在再用free -m命令查看一下内存和swap分区大小,就发现增加了2048M的空间了。创建的是2048,显示1999。
(6)
修改/etc/fstab文件,让CentOS操作系统在每次重启时自动加载/home/swap交换区。上面执行错可以删除交换分区rm /home/swap,对了不用删。停止正在使用的swap分区:swapoff /home/swap。
1.2 安装依赖包及改系统核心参数:关闭一些系统对数据库的限制,pam.d,inittab
yum install -y binutils* compat-libstdc* elfutils-libelf* gcc* glibc* ksh* libaio* libgcc* libstdc* make* sysstat* libXp* glibc-kernheaders
yum install -y ksh binutils compat-libstdc++-33 elfutils-libelf elfutils-libelf-devel gcc gcc-c++ glibc glibc-common glibc-devel libaio libaio-devel libgcc libstdc++ libstdc++-devel make numactl sysstat libXp unixODBC unixODBC-devel
1.vi /etc/sysctl.conf。
在文件最后增加以下行。
fs.file-max = 6815744
fs.aio-max-nr = 1048576
kernel.shmall = 2097152
kernel.shmmax= 2147483648
kernel.shmmni= 4096
kernel.sem = 250 32000100 128
net.ipv4.ip_local_port_range= 9000 65500
net.core.rmem_default= 262144
net.core.rmem_max= 4194304
net.core.wmem_default = 262144
net.core.wmem_max= 1048576
注意,kernel.shmmax参数的值为操作系统内存的一半,单位是字节。例如,装服务器的总物理内存如果是1024MB,那么kernel.shmmax的值应该是512乘1024乘1024=536870912,即kernel.shmmax = 536870912,其它的参数照抄。
2.vi /etc/security/limits.conf。
修改操作系统对oracle用户资源的限制。在该文件中添加如下行:
oracle soft nproc 2047
oracle hard nproc 16384
oracle soft nofile 1024
oracle hard nofile 65536
oracle hard stack 10240
3.vi /etc/pam.d/login。
session required /lib/security/pam_limits.so
4.vi /etc/profile。
if [ $USER = "oracle" ]; thenif [ $SHELL = "/bin/ksh" ]; thenulimit -p 16384ulimit -n 65536elseulimit -u 16384 -n 65536fi
fi
5.vi /etc/selinux/config,修改成 selinux=disabled。
6.关闭图形界面,vi /etc/inittab,把最后一行运行级别改为3,没有的话就不执行这一步。
7.重启服务器 #init 6 或 reboot。
从win本地上传oracle11g1.tgz压缩包到linux的tmp目录。用oracle用户登录,从根目录
下开始解开压缩包,链接:https://pan.baidu.com/s/1Ywtv8zzRGzSCpwu9PyPobQ,提取码:ebk7 。
解压缩包后,一定要退出oracle用户exit
或ctrl+d
,否则oracle用户的环境变量不会生效。oracle11gR2.tgz解压后,会生成/oracle/.bash_profile文件,包括了Oracle数据库的安装参数,内容如下:
export ORACLE_BASE=/oracle/base
export ORACLE_HOME=/oracle/home
export ORACLE_SID=snorcl11g
export NLS_LANG='Simplified Chinese_China.ZHS16GBK'
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/usr/lib
export PATH=$PATH:$HOME/bin:$ORACLE_HOME/bin:.
1.3 数据库的启动和关闭:oracle用户登录,lsnrctl start/stop/status 启动/关闭网络监听服务,dbstart/dbshut 启动/关闭数据库
oracle数据库的启动和关闭配置成系统服务:在操作系统启动/关闭时自动启动/关闭Oracle实例和监听,以下都在root用户操作:
1.
启动数据库实例的SQL脚本:vi /oracle/home/bin/dbstart,chmod +x
sqlplus / as sysdba <<EOF
startup;
EOF
2.
vi /oracle/home/bin/dbrestart,chmod +x
sqlplus / as sysdba <<EOF
shutdown immediate;
startup;
EOF
3.
vi /oracle/home/bin/dbshut,chmod +x
sqlplus / as sysdba <<EOF
shutdown immediate;
EOF
4.
vi /usr/lib/systemd/system/oracle.service(/usr/lib文件夹里有一些.so文件,/systemd/system/oracle.service是自己创建的)
[Unit]
Description=Oracle RDBMS
After=network.target[Service]
Type=simple
ExecStart=/usr/bin/su - oracle -c "/oracle/home/bin/dbstart >> /tmp/oracle.log"
ExecReload=/usr/bin/su - oracle -c "/oracle/home/bin/dbrestart >> /tmp/oracle.log"
ExecStop=/usr/bin/su - oracle -c "/oracle/home/bin/dbshut >> /tmp/oracle.log"
RemainAfterExit=yes[Install]
WantedBy=multi-user.target
vi /usr/lib/systemd/system/lsnrctl.service
[Unit]
Description=Oracle RDBMS
After=network.target[Service]
Type=simple
ExecStart=/usr/bin/su - oracle -c "/oracle/home/bin/lsnrctl start >> /tmp/lsnrctl.log"
ExecReload=/usr/bin/su - oracle -c "/oracle/home/bin/lsnrctl reload >> /tmp/lsnrctl.log"
ExecStop=/usr/bin/su - oracle -c "/oracle/home/bin/lsnrctl stop >> /tmp/lsnrctl.log"
RemainAfterExit=yes[Install]
WantedBy=multi-user.target
5.
如下命令可不执行。
systemctl daemon-reload # 重新加载服务配置文件
systemctl start oracle # 启动oracle服务。
systemctl restart oracle # 重启oracle服务。
systemctl stop oracle # 关闭oracle服务。
systemctl start lsnrctl # 启动lsnrctl服务。
systemctl restart lsnrctl # 重启lsnrctl服务。
systemctl stop lsnrctl # 关闭lsnrctl服务。
6.
systemctl enable oracle # 把Oracle实例服务设置为开机自启动。
systemctl enable lsnrctl # 把Oracle监听服务设置为开机自启动。
7.
Oracle实例启动的日志在/tmp/oracle.log文件中。监听的启动日成在/tmp/lsnrctl.log文件中。只有通过systemctl启动/关闭Oracle实例和监听才会写日志,手工执行脚本不写日志。
1.4 sqlplus命令行登录数据库:用oracle用户登录,执行sqlplus scott/tiger,以scott用户的身份登录
防火墙放开1521端口sqlplus登录数据库,centos7和centos6的防火墙设置不同,centos7采用以下方法:systemctl restart firewalld.service设完端口前后防火墙都重启下,firewall-cmd --list-ports查下放开的端口。
#firewall-cmd --zone=public --add-port=1521/tcp --permanent
启动监听:lsnrctl status
,如下关闭防火墙:
1.5 plsql客户端登录数据库:远程连接需打开监听lsnrctl start
1.客户端环境
:win64_11gR2_client链接:https://pan.baidu.com/s/1xLzrXAZm3xM-szds1IoQFw,提取码:mlo8 ,解压后点击set up应用程序进行安装。
2.客户端界面
:链接:https://pan.baidu.com/s/1H9WIojcMbyqTBZe_goO-1Q 提取码:fp2u ,点击直接安装。
3.客户端参数配置文件
:
D:\app\w\product\11.2.0\client_1\network\admin\tnsnames.ora
snorcl11g_138 =(DESCRIPTION =(ADDRESS_LIST =(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.149.138)(PORT = 1521)))(CONNECT_DATA =(SID = snorcl11g)(SERVER = DEDICATED)))
如上是客户端,如下是服务端,参数内容与上相同,snorcl11g_138:是随便起的。
2.C语言操作oracle:trim,rc,库/头文件
2.1 makefile:-Wno-unused-variable变量未使用关闭警告(如int cc;下面未使用cc变量)
#oracle头文件路径
ORAINCL = -I$(ORACLE_HOME)/rdbms/public# oracle库文件路径 #-L指定库文件的搜索目录/oracle/home/lib
ORALIB = -L$(ORACLE_HOME)/lib -L.# oracle的oci库 #-l指定链接库名libclntsh.so
ORALIBS = -lclntshCFLAGS = -g -Wno-write-strings -Wno-unused-variableall: createtable inserttable selecttable updatetable deletetable execplsqlcreatetable:createtable.cpp _ooci.h _ooci.cppg++ $(CFLAGS) -o createtable createtable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cppinserttable:inserttable.cpp _ooci.h _ooci.cppg++ $(CFLAGS) -o inserttable inserttable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cppselecttable:selecttable.cpp _ooci.h _ooci.cppg++ $(CFLAGS) -o selecttable selecttable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cppupdatetable:updatetable.cpp _ooci.h _ooci.cppg++ $(CFLAGS) -o updatetable updatetable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cppdeletetable:deletetable.cpp _ooci.h _ooci.cppg++ $(CFLAGS) -o deletetable deletetable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cppexecplsql:execplsql.cpp _ooci.h _ooci.cppg++ $(CFLAGS) -o execplsql execplsql.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cpp
-Wno-write-string
没有这行的话可将createtable.cpp中警告的字符串前加上(char*)就行。
还可以在声明时_ooci.h加const,_ooci.cpp中也要修改(不建议)。
如下-g是可调试。
如下全部采用标准写法:头路径,库路径+库名
。
如下采用原始不含头文件编译方法错误。
如下采用ORACLE_HOME换用户时只需要添加该用户.bash_profile中export…进env,不需要改makefile。
下面是库
文件:
如下程序只要链接了动态库就需要LD…。
2.2 createtable.cpp:setenv/putenv设置环境变量,char补空格,varchar不补空格。确定长度用char快,varchar2(4000)用到几个字符自动分配几个而不是一下给4000字符
#include "_ooci.h"
int main(int argc,char *argv[])
{
//11111111111111111111111111111111111111111111111111111111.数据库连接池类connection conn;// 连接数据库,返回值0-成功,其它-失败// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。if (conn.connecttodb("scott/tiger@snorcl11g_138","Simplified Chinese_China.ZHS16GBK") != 0){printf("connect database failed.\n%s\n",conn.m_cda.message); return -1;}//11111111111111111111111111111111111111111111111112.SQL语言操作类sqlstatement stmt(&conn); //不需要判断返回值// 准备创建表的SQL,商品表:商品编号id,商品名称name,价格sal,入库时间btime,商品说明memo,商品图片pic// prepare方法不需要判断返回值stmt.prepare("\create table goods(id number(10),\name varchar2(30),\sal number(10,2),\btime date,\memo clob,\pic blob,\primary key (id))");//111111111111111111111111111111111111111111111113.执行SQL语句,一定要判断返回值,0-成功,其它-失败if (stmt.execute() != 0){printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}printf("create table goods ok.\n");return 0;
}
如下trim(c1)去除c1的空格,最后一行不加trim是查不出下面表格。CLOB:结构化数据如文本信息。BLOB:非结构化数据。
2.3 inserttable.cpp:truncate删除表中全部数据,不产生事务
// 本程序演示向商品表中插入10条记录。
#include "_ooci.h"
// 定义用于操作数据的结构,与表中的字段对应
struct st_GOODS //结构体值与数据库中表即与createtable.cpp中表对应
{long id; // 商品编号,用long数据类型对应oracle无小数的numberchar name[31]; // 商品名称,用char对应oracle的varchar2,注意,表中字段的长度是30,char定义的长度是31,要留C语言的结束符double sal; // 商品价格,用double数据类型对应oracle有小数的numberchar btime[20]; // 入库时间,用char对应oracle的date,格式可以在SQL语句中指定,本程序将指定为yyyy-mm-dd hh24:mi:ss
} stgoods;int main(int argc,char *argv[])
{
//111111111111111111111111111111111111111111111111.数据库连接池connection conn;// 连接数据库,返回值0-成功,其它-失败// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。if (conn.connecttodb("scott/tiger@snorcl11g_138","Simplified Chinese_China.ZHS16GBK") != 0){printf("connect database failed.\n%s\n",conn.m_cda.message); return -1;}//111111111111111111111111111111111111111111111112.SQL语言操作类sqlstatement stmt(&conn);// 准备插入数据的SQL,不需要判断返回值stmt.prepare("\insert into goods(id,name,sal,btime) \values(:1,:2,:3,to_date(:4,'yyyy-mm-dd hh24:mi:ss'))");//to_date(:4,'yyyy-mm-dd hh24:mi:ss')可为null//goods(id,name,sal,btime)字段在createtable.cpp已创建// 为SQL语句绑定输入变量的地址,行stmt.bindin(1,&stgoods.id);//bindin中的1对应上面:1stmt.bindin(2, stgoods.name,30); //30为长度,char是这样绑定stmt.bindin(3,&stgoods.sal); //此时stgoods.id结构体变量们还未赋值stmt.bindin(4, stgoods.btime,19);//111111111111111111111111111111111111111111111113.模拟商品数据,向表中插入10条测试信息for (int ii=1;ii<=10;ii++){// 结构体变量初始化memset(&stgoods,0,sizeof(stgoods));// 为结构体的变量赋值stgoods.id=ii;sprintf(stgoods.name,"商品名称%02d",ii);stgoods.sal=ii*2.11;strcpy(stgoods.btime,"2018-03-01 12:25:31");// 每次指定变量的值后,执行SQL语句,一定要判断返回值,0-成功,其它-失败。if (stmt.execute() != 0) //每执行一次把变量值插入表,返回结构体m_cda.rc{printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}printf("insert ok(id=%d,rpc=%ld).\n",ii,stmt.m_cda.rpc); //rpc为行数}printf("insert table goods ok.\n");// 提交数据库事务,不提交的话程序退出,缺省回滚事务conn.commit();return 0;
}
注释//conn.commit();则insert后查询无记录,如下开启自动提交。
2.4 selecttable.cpp:stmt.m_cda.rpc变量保存了SQL执行后影响的记录数
//本程序演示从商品表中查询数据
#include "_ooci.h"
// 定义用于查询数据的结构,与表中的字段对应
struct st_GOODS //和inserttable.cpp中结构体一样
{long id; // 商品编号,用long数据类型对应oracle无小数的numberchar name[31]; // 商品名称,用char对应oracle的varchar2,注意,表中字段的长度是30,char定义的长度是31,要留C语言的结束符double sal; // 商品价格,用double数据类型对应oracle有小数的numberchar btime[20]; // 入库时间,用char对应oracle的date,格式可以在SQL语句中指定,本程序将指定为yyyy-mm-dd hh24:mi:ss
} stgoods;int main(int argc,char *argv[])
{
//111111111111111111111111111111111111111111111111.数据库连接池connection conn; // 连接数据库,返回值0-成功,其它-失败// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中if (conn.connecttodb("scott/tiger@snorcl11g_138","Simplified Chinese_China.ZHS16GBK") != 0){printf("connect database failed.\n%s\n",conn.m_cda.message); return -1;}//111111111111111111111111111111111111111111111112.SQL语言操作类sqlstatement stmt(&conn);int iminid,imaxid;// 准备查询数据的SQL,不需要判断返回值stmt.prepare("\select id,name,sal,to_char(btime,'yyyy-mm-dd hh24:mi:ss') from goods where id>:1 and id<:2");//id,name,sal,to_char(btime,'yyyy-mm-dd hh24:mi:ss')是输出变量//id>:1 and id<:2是输入变量 // 为SQL语句绑定输入变量的地址stmt.bindin(1,&iminid);stmt.bindin(2,&imaxid);// 为SQL语句绑定输出变量的地址// stgoods.id这些变量在inserttable已经赋过值stmt.bindout(1,&stgoods.id); stmt.bindout(2, stgoods.name,30);stmt.bindout(3,&stgoods.sal);stmt.bindout(4, stgoods.btime,19);// 手工指定id的范围为1到5,执行一次查询 //inserttable已插入10条记录iminid=1;imaxid=5;//111111111111111111111111111111111111111111111113.执行SQL语句,一定要判断返回值,0-成功,其它-失败if (stmt.execute() != 0){printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}//上面执行完sql语句就得到一个结果集,怎么获取这结果集呢?用next方法while (1){// 先把结构体变量初始化,然后才获取记录memset(&stgoods,0,sizeof(stgoods));// 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败// 在实际应用中,除了0和1403,其它的情况极少出现。if (stmt.next() !=0) break;//每next一次就从结果集里拿一条数据 // 把获取到的记录的值打印出来printf("id=%ld,name=%s,sal=%.02f,btime=%s\n",stgoods.id,stgoods.name,stgoods.sal,stgoods.btime);}printf("本次查询了goods表%ld条记录。\n",stmt.m_cda.rpc); return 0;
}
2.5 updatetable.cpp:连接数据库,返回值0成功,其它失败,失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中
// 本程序演示更新商品表中数据
#include "_ooci.h"
int main(int argc,char *argv[])
{
//111111111111111111111111111111111111111111111111.数据库连接池connection conn; if (conn.connecttodb("scott/tiger@snorcl11g_138","Simplified Chinese_China.ZHS16GBK") != 0){printf("connect database failed.\n%s\n",conn.m_cda.message); return -1;}//111111111111111111111111111111111111111111111112.SQL语言操作类sqlstatement stmt(&conn);int iminid,imaxid;char strbtime[20];// 准备更新数据的SQL,不需要判断返回值stmt.prepare("\update goods set btime=to_date(:1,'yyyy-mm-dd hh24:mi:ss') where id>:2 and id<:3");// 为SQL语句绑定输入变量的地址stmt.bindin(1, strbtime,19); //1对应上面:1stmt.bindin(2,&iminid); //2对应上面:2stmt.bindin(3,&imaxid);// 手工指定id的范围为1到5,btime为2017-12-20 09:45:30,执行一次更新iminid=1;imaxid=5;memset(strbtime,0,sizeof(strbtime));strcpy(strbtime,"2017-12-20 09:45:30");//111111111111111111111111111111111111111111111113.执行SQL语句,一定要判断返回值,0-成功,其它-失败。if (stmt.execute() != 0){printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}// 请注意,stmt.m_cda.rpc变量非常重要,它保存了SQL被执行后影响的记录数。printf("本次更新了goods表%ld条记录。\n",stmt.m_cda.rpc);// 提交事务conn.commit();return 0;
}
2.6 deletetable.cpp:删除商品表中数据
#include "_ooci.h"
int main(int argc,char *argv[])
{
//111111111111111111111111111111111111111111111111.数据库连接池connection conn; if (conn.connecttodb("scott/tiger@snorcl11g_138","Simplified Chinese_China.ZHS16GBK") != 0){printf("connect database failed.\n%s\n",conn.m_cda.message); return -1;}//111111111111111111111111111111111111111111111112.SQL语言操作类sqlstatement stmt(&conn);int iminid,imaxid; // 准备删除数据的SQL,不需要判断返回值stmt.prepare("delete from goods where id>:1 and id<:2");// 为SQL语句绑定输入变量的地址stmt.bindin(1,&iminid);stmt.bindin(2,&imaxid);// 手工指定id的范围为1到5iminid=1;imaxid=5;//111111111111111111111111111111111111111111113.执行SQL语句,一定要判断返回值,0-成功,其它-失败if (stmt.execute() != 0){printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}printf("本次从goods表中删除了%ld条记录。\n",stmt.m_cda.rpc); conn.commit();return 0;
}
2.7 execplsql.cpp:执行一个PL/SQL过程
// execplsql.cpp:其中sql:删除全部记录并插入一行
// 在这里说一下我个人的意见,我从不在Oracle数据库中创建PL/SQL过程,也很少使用触发器,原因如下:
// 1、在Oracle数据库中创建PL/SQL过程,程序的调试很麻烦;
// 2、维护工作很麻烦,因为程序员要花时间去了解数据库中的存储过程;
// 3、在封装的oci中,对oracle的操作已经是简单的事情,没必要去折腾存储过程;
// 在oci中也很少用PL/SQL语句,也是因为复杂的PL/SQL调试麻烦。
#include "_ooci.h"int main(int argc,char *argv[])
{
//111111111111111111111111111111111111111111111111.数据库连接池connection conn; // 连接数据库,返回值0-成功,其它-失败// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。if (conn.connecttodb("scott/tiger@snorcl11g_138","Simplified Chinese_China.ZHS16GBK") != 0){printf("connect database failed.\n%s\n",conn.m_cda.message); return -1;}//111111111111111111111111111111111111111111111112.SQL语言操作类sqlstatement stmt(&conn);// 准备删除记录的PL/SQL,不需要判断返回值// 本PL/SQL先删除goods表中的全部记录,再插入一条记录stmt.prepare("\BEGIN\delete from goods;\insert into goods(id,name,sal,btime)\values(:1,'过程商品',55.65,to_date('2018-01-02 13:00:55','yyyy-mm-dd hh24:mi:ss'));\END;");//上面2条sql语句,:1是为了绑定变量,:1换成100就没有下面两行了int id=100;stmt.bindin(1,&id);// 注意,PL/SQL中的每条SQL需要用分号结束,END之后还有一个分号。//111111111111111111111111111111111111111111111113.执行SQL语句,一定要判断返回值,0-成功,其它-失败if (stmt.execute() != 0){printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}printf("exec PL/SQL ok.\n");conn.commit();return 0;
}
3.虚表/日期/序列:ntp&hwclock
oracle的虚表dual用来构成select的语法规则
,不要当表来看,oracle保证dual里永远只有一条记录,可用它来做如下很多事。
如下若是c语言写1/86400就是=0(因为整数除),只有浮点数才能这样运算。oracle中是非常精确的,不用担心精度问题。
如下sysdate函数返回值可以当成值来用。
SQL>CREATE SEQUENCE ABC INCREMENT BY 1 START WITH 1 MAXVALUE 9999999999 NOCYCLE NOCACHE;
如上新建一个序列第一次取当前值是取不到的(提示该序列在此会话中未定义),必须.nextval再.currval取当前值。下面起步为100,步长为5。
下面给表增加两个字段crttime和keyid(导出数据时用到keyid),下面在crttable.sql中。
rowid
指物理位置。rownum
指序号(C语言很少用rownum)。如下第一行select时需要将rownum和rowid写出来查询才能看到,第二行用rowid作为查询或更新的条件最快(区别索引)
如上每条记录虽然存在数据库里(即表空间,表空间还是数据文件)最后还是存在磁盘上(在磁盘上就有位置),就是/oracle/base/oradata/snorcl11g里的.dbf文件。
4.索引/视图/链路/同义词:create index/view/link/synonym,distinct
ddatetime在数据库中是整数
。
视图就是访问数据的一个窗口,当成一个表来用,下面为先授权。
如上可以exit退出,也可直接切换connect scott用户,drop view …
视图不占用表空间,在emp将hiredate设置为系统时间,从v_emp查也有效果。
下面是同义词
:数据库对象(表,视图,序列,存储过程,包)的一个别名。实际不可能给shqx和密码给客户,给普通用户如scott给客户并授权。
对表使用别名,如下内表是dept,外表是emp。
distinct关键字,取结果集中的唯一值,去重,如下有很多30,20,10重复。
5.系统高可用性:12个1T组合,rac
如下假如oracle数据库在一个服务器上运行,出问题了怎么办?(一般硬件故障)。Oracle三种高可用集群方案:1.RAC,2.Data Guard,3.OGG
。如下是RAC技术,可以多个,但一般为两个。有的数据在内存,有的在缓存,2个要进行数据协调很麻烦,所以不是高性能,比单个服务器要慢。
如下为结合三种集群技术应用。
RAC有多个节点(上面为两个),应用程序只要做好客户端配置怎么连RAC数据库就行。
6.txt/xml文件入表:结构体内容
//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){
//111111111111111111111111111111111111111111111扫描数据文件存放的目录,只匹配"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;}//11111111111111111111111111111111111111111111111连接数据库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);//111111111111111111111111111111111111111111111111处理入库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);
}//11111111111111111111111111111111111111111111111处理入库
bool _psurfdata()
{CFile File;if (File.Open(Dir.m_FullFileName,"r")==false){logfile.Write("(File.Open(%s) failed.\n",Dir.m_FullFileName); return false;}//1111111111111111111111111111读取文件中的每一行记录,写入数据库的表中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){
//1111111111111111111111111111111111111111111扫描数据文件存放的目录,匹配"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;}//111111111111111111111111111111111111111111111逐个处理目录中的数据文件while (true){if (Dir.ReadDir()==false) break;//111111111111111111111111111111111111111111111连接数据库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);//111111111111111111111111111111111111111111111处理入库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);
}//111111111111111111111111111111111111111111111处理入库
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(); 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;
}
//_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/sint r; // 降雨量:0.1mmint vis; // 能见度:0.1米
};
struct st_signallog //分区信号数据结构
{char obtid[11];char ddatetime[20];char signalname[2];char signalcolor[2];
};//111111111111111111111111111111111111111111111111CSURFDATA类
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();
};//1111111111111111111111111111111111111111111111CSIGNALLOG类
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; bool SplitBuffer(const char *strBuffer); // 把记录拆分到vsignallog容器中。 long InsertTable(); // 把vsignallog容器中的值更新到T_SIGNALDATA表中。
};//11111111111111111111111111111111111111111把非结构化数据文件写入oracle数据库的表中
int FileToTable(connection *in_conn,CLogFile *in_logfile,char *in_tname,char *in_filename,char *in_ddatetime);
#endif
//_shqx.cpp
#include "_shqx.h"
//11111111111111111111111111111111111111111111111111111CSURFDATA类
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()
{
}bool CSURFDATA::SplitBuffer(const char *strBuffer) //把用逗号分隔的记录拆分到m_stsurfdata结构中
{ 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/sCmdStr.GetValue(7,&dtmp); m_stsurfdata.r=(int)(dtmp*10); // 降雨量:0.1mmCmdStr.GetValue(8,&dtmp); m_stsurfdata.vis=(int)(dtmp*10); // 能见度:0.1米return true;
}bool CSURFDATA::SplitBuffer1(const char *strBuffer) //把xml格式的记录拆分到m_stsurfdata结构中
{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/sGetXMLBuffer(strBuffer,"r",&dtmp); m_stsurfdata.r=(int)(dtmp*10); // 降雨量:0.1mmGetXMLBuffer(strBuffer,"vis",&dtmp); m_stsurfdata.vis=(int)(dtmp*10); // 能见度:0.1米return true;
}long CSURFDATA::InsertTable() //把m_stsurfdata结构中的值更新到T_SURFDATA表中
{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) {if (stmtupt.execute() != 0) //执行更新的SQL语句,一定要判断返回值,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{ if (stmtins.execute() != 0) // 执行插入的SQL语句,一定要判断返回值,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;
}//1111111111111111111111111111111111111111111111111111111111111CSIGNALLOG类
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()
{
}bool CSIGNALLOG::SplitBuffer(const char *strBuffer) //把记录拆分到vsignallog容器中
{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); //数据时间:格式yyyymmddhh24mistrcat(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]就是Am_stsignallog.signalcolor[0]=strtemp[1]; //strtemp[1]就是3vsignallog.push_back(m_stsignallog);totalcount++;}return true;
}long CSIGNALLOG::InsertTable() //把vsignallog容器中的值更新到T_SIGNALDATA表中
{ //可能会返回stmtupt.m_cda.rc,所以用longif (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) { if (stmtupt.execute() != 0) //执行更新的SQL语句,一定要判断返回值,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{ if (stmtins.execute() != 0) //执行插入的SQL语句,一定要判断返回值,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;
}//11111111111111111111111111111111111111111111111把非结构化数据文件写入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(); if (icount>0) return 0; //如果记录已存在,直接返回0-成功。 //11111111111111111111111111111111111111111111111111111111111111111int 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;} //111111111111111111111111111111111111111111111111111111111111111stmt.prepare("select filecontent from %s where filename=:1 for update",in_tname);stmt.bindin(1,in_filename,300); //把文件内容更新到BLOB字段中。stmt.bindblob();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;} if (stmt.next() != 0) return -1; //获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败 if (stmt.filetolob((char *)in_filename) != 0) { //把磁盘文件pic_in.jpg的内容写入BLOB字段,一定要判断返回值,0-成功,其它-失败。in_logfile->Write("FileToTable() stmt.filetolob() failed.\n%s\n",stmt.m_cda.message); return -1;} in_conn->commit(); //图片数据大,一个文件提交一次return 0;
}
创建新用户并指定该用户缺省表空间为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;
下面是以超级用户登录,/
指不用任何密码,不能远程登录只能登录本机且装有数据库。
7.站点参数建表入表/PowerDesigner/主外键:多表查询
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 。
P:主键,F:外键,M:勾上不允许为空。
1.
下面为主键命名
,主键在Columns里p字段
已指定。
如下是主键的另一种写法。
2.
下面为创建索引
。
3.
下面为创建表空间
,如下将表存入名为USERS的表空间。
如下将索引存入名为INDEXS表空间。
如下主键就是索引也有这些参数也扔入INDEXS表空间。
4.
字段值附上约束条件
,如果复制其他表记得删除字段值约束条件
。
5.
如下U是unique index
,记录序号即keyid是唯一约束,第三行是数据时间。
下面是创建序列。
ctrl+G生成.sql文件,注意主键名不要重复,双引号替换为空。复制刚生成的.sql文件内容到crttable.sql中,执行如下命令必须其他窗口SQL>exit。
8.数据交换:数据导出为文件,数据文件推送
// exptables.cpp
#include "_public.h"
#include "_ooci.h"
struct st_arg // 主程序的参数
{char connstr[101];char charset[51];char tname[51];char cols[1001];char fieldname[1001];char fieldlen[501];int exptype;char andstr[501];char bname[51];char ename[51];char taskname[51];char exppath[301];int timetvl;
} starg;
CLogFile logfile;
connection conn;
bool _exptables(); // 本程序的业务流程主函数
void EXIT(int sig);
vector<string> vfieldname; // 存放拆分fieldname后的容器
vector<int> vfieldlen; // 存放拆分fieldlen后的容器
int maxfieldlen; // 存放fieldlen的最大值
void SplitFields(); // 拆分fieldname和fieldlen
void _help(char *argv[]); // 显示程序的帮助
long maxkeyid; // 已导出数据的最大的keyid
bool LoadMaxKeyid(); // 从系统参数T_SYSARG表中加载已导出数据的最大的keyid
bool UptMaxKeyid(); // 更新系统参数T_SYSARG表中已导出数据的最大的keyid
bool _xmltoarg(char *strxmlbuffer); // 把xml解析到参数starg结构中int main(int argc,char *argv[])
{if (argc!=3) { _help(argv); return -1; }CloseIOAndSignal();signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[1],"a+")==false){printf("打开日志文件失败(%s)。\n",argv[1]); return -1;} if (_xmltoarg(argv[2])==false) return -1; // 把xml解析到参数starg结构中while (true){ // 连接数据库 //放while (true)外面连耗资源 if (conn.connecttodb(starg.connstr,starg.charset) != 0){logfile.Write("connect database %s failed.\n",starg.connstr); sleep(starg.timetvl); continue;}// logfile.Write("export table %s.\n",starg.tname);if (_exptables() == false) logfile.Write("export tables failed.\n"); //导出数据的主函数conn.disconnect(); // 断开与数据库的连接sleep(starg.timetvl);}return 0;
}
void EXIT(int sig)
{logfile.Write("程序退出,sig=%d\n\n",sig);exit(0);
}//1111111111111111111111111111111111111111111111111111111111111111111111
void _help(char *argv[])
{printf("\n");printf("Using:/htidc/public/bin/exptables logfilename xmlbuffer\n\n");printf("增量导出示例:\n");printf("/htidc/public/bin/exptables /log/shqx/exptables_surfdata_for_hb.log \"<connstr>shqx/pwdidc@snorcl11g_198</connstr><charset>Simplified Chinese_China.ZHS16GBK</charset><tname>T_SURFDATA</tname><cols>obtid,to_char(ddatetime,'yyyymmddhh24miss'),t,p,u,wd,wf,r,vis</cols><fieldname>obtid,ddatetime,t,p,u,wd,wf,r,vis</fieldname><fieldlen>5,14,8,8,8,8,8,8,8</fieldlen><exptype>1</exptype><andstr> and obtid in ('59293','50745')</andstr><bname>SURFDATA_</bname><ename>_for_hb</ename><taskname>SURFDATA_FOR_HB</taskname><exppath>/data/shqx/exp/tohb</exppath><timetvl>30</timetvl>\"\n\n");printf("全量导出示例:\n");printf("/htidc/public/bin/exptables /log/shqx/exptables_obtcode_for_hb.log \"<connstr>shqx/pwdidc@snorcl11g_198</connstr><charset>Simplified Chinese_China.ZHS16GBK</charset><tname>T_OBTCODE</tname><cols>obtid,obtname,provname,lat,lon,height</cols><fieldname>obtid,obtname,provname,lat,lon,height</fieldname><fieldlen>5,30,30,8,8,8</fieldlen><exptype>2</exptype><andstr> and rsts=1 and obtid in ('59293','50745')</andstr><bname>OBTCODE_</bname><ename>_for_hb</ename><exppath>/data/shqx/exp/tohb</exppath><timetvl>300</timetvl>\"\n\n");printf("本程序是数据中心的公共功能模块,从数据库的表中导出数据生成xml文件,用于数据交换。\n");printf("logfilename是本程序运行的日志文件。\n");printf("xmlbuffer为文件传输的参数,如下:\n");printf("数据库的连接参数 <connstr>shqx/pwdidc@snorcl11g_198</connstr>\n");printf("数据库的字符集 <charset>Simplified Chinese_China.ZHS16GBK</charset> 这个参数要与数据源数据库保持>一致,否则会出现中文乱码的情况。\n");printf("待导出数据的表名 <tname>T_SURFDATA</tname>\n");printf("需要导出字段的列表 <cols>obtid,to_char(ddatetime,'yyyymmddhh24miss'),t,p,u,wd,wf,r,vis</cols> 可以采用函数。\n");printf("导出字段的别名列表 <fieldname>obtid,ddatetime,t,p,u,wd,wf,r,vis</fieldname> 必须与cols一一对应。\n");printf("导出字段的长度列表 <fieldlen>5,14,8,8,8,8,8,8,8</fieldlen> 必须与cols一一对应。\n");printf("导出数据的方式 <exptype>1</exptype> 1-增量导出;2-全量导出,如果是增量导出,要求表一定要有keyid字段。\n");printf("导出数据的附加条件 <andstr> and obtid in ('59293','50745')</andstr> 注意,关键字and不能少。\n");printf("导出文件的命名的前部分 <bname>SURFDATA_</bname>\n");printf("导出文件的命名的后部分 <ename>_for_hb</ename>\n");printf("导出任务的命名 <taskname>SURFDATA_FOR_HB</taskname> 当exptype=1时该参数有效。\n");printf("导出文件存放的目录 <exppath>/data/shqx/exp/tohb</exppath>\n");printf("导出数据的时间间隔 <timetvl>30</timetvl> 单位:秒,建议大于10。\n");printf("以上参数,除了taskname和andstr,其它字段都不允许为空。\n\n\n");
}bool _xmltoarg(char *strxmlbuffer)
{memset(&starg,0,sizeof(struct st_arg));GetXMLBuffer(strxmlbuffer,"connstr",starg.connstr);if (strlen(starg.connstr)==0) { logfile.Write("connstr is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"charset",starg.charset);if (strlen(starg.charset)==0) { logfile.Write("charset is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"tname",starg.tname);if (strlen(starg.tname)==0) { logfile.Write("tname is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"cols",starg.cols);if (strlen(starg.cols)==0) { logfile.Write("cols is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"fieldname",starg.fieldname);if (strlen(starg.fieldname)==0) { logfile.Write("fieldname is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"fieldlen",starg.fieldlen);if (strlen(starg.fieldlen)==0) { logfile.Write("fieldlen is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"exptype",&starg.exptype);if ( (starg.exptype!=1) && (starg.exptype!=2) ) { logfile.Write("exptype is not in (1,2).\n"); return false; }GetXMLBuffer(strxmlbuffer,"andstr",starg.andstr);if (strlen(starg.andstr)==0) { logfile.Write("andstr is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"bname",starg.bname);if (strlen(starg.bname)==0) { logfile.Write("bname is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"ename",starg.ename);if (strlen(starg.ename)==0) { logfile.Write("ename is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"taskname",starg.taskname);if ( (starg.exptype==1) && (strlen(starg.taskname)==0) ) { logfile.Write("taskname is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"exppath",starg.exppath);if (strlen(starg.exppath)==0) { logfile.Write("exppath is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"timetvl",&starg.timetvl);if (starg.timetvl==0) { logfile.Write("timetvl is null.\n"); return false; }// 拆分fieldname和fieldlenSplitFields();// 判断fieldname和fieldlen中元素的个数一定要相同if (vfieldname.size() != vfieldlen.size() ) { logfile.Write("fieldname和fieldlen的元素个数不同。.\n"); return false; }return true;
}//111111111111111111111111111111111111111本程序的业务流程主函数
bool _exptables()
{// 从系统参数T_SYSARG表中加载已导出数据的最大的keyidif (LoadMaxKeyid()==false) { logfile.Write("LoadMaxKeyid() failed.\n"); return false; } // 生成导出数据的SQL语句char strsql[4096]; char fieldvalue[vfieldname.size()][maxfieldlen+1]; // 输出变量定义为一个二维数组//第一维vfieldname.size()字段个数(限制fieldvalue外个数),第二维maxfieldlen+1字段长度(限制fieldvalue内个数),+1是最后一个空字符结尾符//导出数据的结果不管是字符串,整数还是浮点数都用字符串存放memset(strsql,0,sizeof(strsql));if (starg.exptype==1) //增量导出, order by keyid排完序后数据好导入sprintf(strsql,"select %s,keyid from %s where 1=1 and keyid>%ld %s order by keyid",starg.cols,starg.tname,maxkeyid,starg.andstr);else //全量导出不需要keyidsprintf(strsql,"select %s from %s where 1=1 %s",starg.cols,starg.tname,starg.andstr);sqlstatement stmt(&conn);stmt.prepare(strsql); for (int ii=0;ii<vfieldname.size();ii++){stmt.bindout(ii+1,fieldvalue[ii],vfieldlen[ii]); //绑定变量从1开始算}// 如果是增量导出,还要绑定keyid字段if (starg.exptype==1) stmt.bindout(vfieldname.size()+1,&maxkeyid); if (stmt.execute() != 0) // 执行导出数据的SQL{logfile.Write("select %s failed.\n%s\n%s\n",starg.tname,stmt.m_cda.message,stmt.m_sql); return false;}int iFileSeq=1; // 待生成文件的序号,临时变量,文件名不会重复了char strFileName[301],strLocalTime[21];CFile File;while (true){ // 如果在循环外面打开文件,stmt.next若是没记录又要删除文件memset(fieldvalue,0,sizeof(fieldvalue)); if (stmt.next() !=0) break; if (File.IsOpened()==false) // 把数据写入文件{memset(strLocalTime,0,sizeof(strLocalTime));LocalTime(strLocalTime,"yyyymmddhh24miss");memset(strFileName,0,sizeof(strFileName));sprintf(strFileName,"%s/%s%s%s_%d.xml",starg.exppath,starg.bname,strLocalTime,starg.ename,iFileSeq++);if (File.OpenForRename(strFileName,"w")==false){logfile.Write("File.OpenForRename(%s) failed.\n",strFileName); return false;}File.Fprintf("<data>\n");}for (int ii=0;ii<vfieldname.size();ii++){ //数据一个字段一个字段写入xml文件中File.Fprintf("<%s>%s</%s>",vfieldname[ii].c_str(),fieldvalue[ii],vfieldname[ii].c_str());}File.Fprintf("<endl/>\n");//111111111111111111111111111111111111111111111111111111111111if (stmt.m_cda.rpc%1000==0) //每写入1000行关闭文件{File.Fprintf("</data>\n");if (File.CloseAndRename()==false){logfile.Write("File.CloseAndRename(%s) failed.\n",strFileName); return false;}// 更新系统参数T_SYSARG表中已导出数据的最大的keyidif (UptMaxKeyid()==false) { logfile.Write("UptMaxKeyid() failed.\n"); return false; }logfile.Write("create file %s ok.\n",strFileName);}}//1111111111111111111111111111111111111111111111111111111111if (File.IsOpened()==true) //不够1000条的写入一个文件{File.Fprintf("</data>\n");if (File.CloseAndRename()==false){logfile.Write("File.CloseAndRename(%s) failed.\n",strFileName); return false;}// 更新系统参数T_SYSARG表中已导出数据的最大的keyidif (UptMaxKeyid()==false) { logfile.Write("UptMaxKeyid() failed.\n"); return false; }logfile.Write("create file %s ok.\n",strFileName);}if (stmt.m_cda.rpc>0) logfile.Write("本次导出了%d条记录。\n",stmt.m_cda.rpc);return true;
}//1111111111111111111111111111111111111111111111111111
void SplitFields() //拆分fieldname和fieldlen
{vfieldname.clear(); vfieldlen.clear(); maxfieldlen=0; CCmdStr CmdStr;CmdStr.SplitToCmd(starg.fieldname,",");vfieldname.swap(CmdStr.m_vCmdStr);int ifieldlen=0;CmdStr.SplitToCmd(starg.fieldlen,",");for (int ii=0;ii<CmdStr.CmdCount();ii++){ CmdStr.GetValue(ii,&ifieldlen); //maxfieldlen一开始为0if (ifieldlen>maxfieldlen) maxfieldlen=ifieldlen; //得到fieldlen的最大值maxfieldlenvfieldlen.push_back(ifieldlen);}
}//111111111111111111111111111111111从系统参数T_SYSARG表中加载已导出数据的最大的keyid
bool LoadMaxKeyid() //实现增量采集必须把每次导出数据的keyid保存起来,所以采用一个T_SYSARG参数表
{ //taskname作为参数代码argcodeif (starg.exptype!=1) return true; //只有增量导出才需要加载/更新系统参数表sqlstatement stmt(&conn);stmt.prepare("select argvalue from T_SYSARG where argcode=:1");stmt.bindin(1,starg.taskname,50);stmt.bindout(1,&maxkeyid);if (stmt.execute() != 0){logfile.Write("select T_SYSARG failed.\n%s\n%s\n",stmt.m_cda.message,stmt.m_sql); return false;}// 如果记录不存在,插入一新记录。if (stmt.next() != 0){ //一直只有一条记录:SURFDATA_FOR_HB,SURFDATA_FOR_HB,0stmt.prepare("insert into T_SYSARG(argcode,argname,argvalue) values(:1,:2,0)");stmt.bindin(1,starg.taskname,50);stmt.bindin(2,starg.taskname,50);stmt.execute();conn.commit();}// logfile.Write("maxkeyid=%d\n",maxkeyid);return true;
}//1111111111111111111111111111111111更新系统参数T_SYSARG表中已导出数据的最大的keyid
bool UptMaxKeyid() //导出前加载,导出后更新
{if (starg.exptype!=1) return true;sqlstatement stmt(&conn);stmt.prepare("update T_SYSARG set argvalue=:1 where argcode=:2");stmt.bindin(1,&maxkeyid);stmt.bindin(2,starg.taskname,50);if (stmt.execute() != 0){logfile.Write("select T_SYSARG failed.\n%s\n%s\n",stmt.m_cda.message,stmt.m_sql); return false;}conn.commit();return true;
}
下面为全量
导出站点参数
表。
vi …1.xml,只导出了2条记录一个文件。
程序在后台跑,有新图就拿下来,wgetclient将网页内容全弄下来,搞清图片命名规律进行解析。
//wgetclient.cpp
#include "_public.h"
void EXIT(int sig);
CLogFile logfile;
int main(int argc, char *argv[])
{if(argc!=6){printf("Usage:%s weburl tmpfilename outputfilename logfilename charset\n",argv[0]); printf("本程序用于获取WEB网页的内容。\n");printf("weburl 网页WEB的地址。\n");printf("tmpfilename 获取到的网页的内容存放的全路径的临时文件名,该文件可能是utf-8或其它编码。\n");printf("outputfilename 最终的输出文件全路径文件名,该文件是gb18030编码,注意tmpfilename被转换为outputfilename后,tmpfilename文件被自动删除。\n");printf("logfilename 本程序的运行产生的日志文件名。\n");printf("charset 网页的字符集,如utf-8\n\n");exit(1);}// 关闭全部的信号和输入输出// 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程// 但请不要用 "kill -9 +进程号" 强行终止CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[4],"a+") == false){printf("logfile.Open(%s) failed.\n",argv[4]); return -1;}MKDIR(argv[2],true); MKDIR(argv[3],true);char strweburl[3001];memset(strweburl,0,sizeof(strweburl));strncpy(strweburl,argv[1],3000);char strcmd[3024];memset(strcmd,0,sizeof(strcmd));snprintf(strcmd,3000,"/usr/bin/wget -c -q -O %s \"%s\" 1>>/dev/null 2>>/dev/null",argv[2],strweburl);system(strcmd);logfile.Write("%s\n",strcmd);char strfilenametmp[301];memset(strfilenametmp,0,sizeof(strfilenametmp));snprintf(strfilenametmp,300,"%s.tmp",argv[3]);// 把获取到的网页转换为中文memset(strcmd,0,sizeof(strcmd));snprintf(strcmd,256,"iconv -c -f %s -t gb18030 %s -o %s",argv[5],argv[2],strfilenametmp);system(strcmd);logfile.Write("%s\n",strcmd);REMOVE(argv[2]); // 删除临时文件 RENAME(strfilenametmp,argv[3]);return 0;
}void EXIT(int sig)
{if (sig > 0) signal(sig,SIG_IGN);logfile.Write("catching the signal(%d).\n",sig);logfile.Write("wgetclient exit.\n");exit(0);
}
//wgetrain24.cpp
#include "_public.h"
void EXIT(int sig);
CLogFile logfile;
bool GetURL(char *strBuffer,char *strURL,char *strFileName);
int main(int argc, char *argv[])
{if(argc!=4){printf("Usage:%s logfilename tmpfilename outputfilename\n",argv[0]); printf("Sample:./wgetrain24 /log/shqx/wgetrain24.log /data/wgettmp /data/wfile/zhrain24\n\n");printf("本程序用于从中国天气网获取逐小时降雨量实况图。\n");printf("中国天气网的url是http://products.weather.com.cn/product/Index/index/procode/JC_JSL_ZH.shtml\n");printf("如果中国天气网的url改变,程序也在做改动。\n");printf("logfilename 本程序的运行产生的日志文件名。\n");printf("tmpfilename 本程序运行产生的临时文件存放的目录。\n");printf("获取逐小时降雨量实况图存放的目录。\n\n");exit(1);}CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[1],"a+") == false){printf("logfile.Open(%s) failed.\n",argv[1]); return -1;}MKDIR(argv[2],false); MKDIR(argv[3],false);while (true){ char strwgetclient[2001]; // 调用wgetclient获取网页内容memset(strwgetclient,0,sizeof(strwgetclient));snprintf(strwgetclient,2000,"/htidc/public/bin/wgetclient \"http://products.weather.com.cn/product/Index/index/procode/JC_JSL_ZH.shtml\" %s/wgetclient_%d.tmp %s/wgetclient_%d.html %s/wgetclient.log utf-8",argv[2],getpid(),argv[2],getpid(),argv[2]);system(strwgetclient);// logfile.Write("%s\n",strwgetclient);// 打开网页内容文件char stroutputfile[301];memset(stroutputfile,0,sizeof(stroutputfile));snprintf(stroutputfile,300,"%s/wgetclient_%d.html",argv[2],getpid());CFile File;if (File.Open(stroutputfile,"r")==false){logfile.Write("File.Open(%s) failed.\n",stroutputfile); sleep(60); continue;} char strBuffer[1001],strURL[501],strFullFileName[301],strFileName[101]; // 得到全部的图片文件名while (true){memset(strBuffer,0,sizeof(strBuffer));memset(strURL,0,sizeof(strURL));memset(strFullFileName,0,sizeof(strFullFileName));memset(strFileName,0,sizeof(strFileName)); if (File.Fgets(strBuffer,1000)==false) break; if (MatchFileName(strBuffer,"*PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20*.JPG*")==false) continue; // logfile.Write("%s",strBuffer); if (GetURL(strBuffer,strURL,strFileName)==false) continue; //解析出url和文件名 snprintf(strFullFileName,300,"%s/%s",argv[3],strFileName); //文件已存在,不采集if (access(strFullFileName,F_OK)==0) continue; logfile.Write("download %s ",strFileName); //调用wget获取文件memset(strwgetclient,0,sizeof(strwgetclient));snprintf(strwgetclient,500,"wget \"%s\" -o %s/wgetrain24.log -O %s",strURL,argv[2],strFullFileName);system(strwgetclient); if (access(strFullFileName,F_OK)==0) logfile.WriteEx("ok.\n");else logfile.WriteEx("failed.\n");} File.CloseAndRemove();sleep(60);}return 0;
}bool GetURL(char *strBuffer,char *strURL,char *strFileName)
{char *start,*end;start=end=0;if ((start=strstr(strBuffer,"http"))==0) return false;if ((end=strstr(start,"\""))==0) return false; //找双引号strncpy(strURL,start,end-start);strcpy(strFileName,strstr(strURL,"PWCP"));return true;
}
void EXIT(int sig)
{if (sig > 0) signal(sig,SIG_IGN);logfile.Write("catching the signal(%d).\n",sig);logfile.Write("wgetclient exit.\n");exit(0);
}
9.非结构化数据存储:blob,pzhrain24file
// 本程序演示如何把磁盘文件的文本文件写入Oracle的BLOB字段中。
//filetoblob.cpp,实时生成的不要存oracle的blob字段
#include "_ooci.h"
int main(int argc,char *argv[])
{connection conn; // 连接数据库,返回值0-成功,其它-失败// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。if (conn.connecttodb("scott/tiger@snorcl11g_198","Simplified Chinese_China.ZHS16GBK") != 0){printf("connect database %s failed.\n%s\n","scott/tiger@orcl",conn.m_cda.message); return -1;} sqlstatement stmt(&conn); // SQL语言操作类 // 为了方便演示,把goods表中的记录全删掉,再插入一条用于测试的数据。// 不需要判断返回值stmt.prepare("\BEGIN\delete from goods;\insert into goods(id,name,pic) values(1,'商品名称',empty_blob());\END;"); // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。if (stmt.execute() != 0){printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}// 使用游标从goods表中提取id为1的pic字段// 注意了,同一个sqlstatement可以多次使用// 但是,如果它的sql改变了,就要重新prepare和bindin或bindout变量stmt.prepare("select pic from goods where id=1 for update");stmt.bindblob();// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。if (stmt.execute() != 0){printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}// 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。if (stmt.next() != 0) return 0; // 把磁盘文件pic_in.jpg的内容写入BLOB字段,一定要判断返回值,0-成功,其它-失败。if (stmt.filetolob((char *)"pic_in.jpg") != 0){printf("stmt.filetolob() failed.\n%s\n",stmt.m_cda.message); return -1;} conn.commit(); // 提交事务return 0;
}
如下文件信息放入表中。
// pzhrain24file.cpp
#include "_public.h"
#include "_ooci.h"
#include "_shqx.h"
CLogFile logfile;
CDir Dir;
// 处理数据文件
bool _pzhrain24file(char *strargv2,char *strargv4,char *strargv5);
connection conn;
void EXIT(int sig);int main(int argc,char *argv[])
{if (argc!=7){printf("\n本程序用于处理全国逐小时雨量实况图片文件。\n\n");printf("/htidc/shqx/bin/pzhrain24file logfilename connstr srcpathname dstpathname tname timetvl\n");printf("例如:/htidc/shqx/bin/pzhrain24file /log/shqx/pzhrain24file.log shqx/pwdidc@snorcl11g_198 /data/wfile/zhrain24 /qxfile/zhrain24 T_ZHRAIN24 30\n");printf("logfilename 本程序运行的日志文件名。\n");printf("connstr 数据库的连接参数。\n");printf("srcpathname 原始文件存放的目录,文件命名如PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20191101070000000.JPG。\n");printf("dstpathname 目标文件存放的目录,文件按yyyy/mm/dd组织目录,重命名为zhrain24_yyyymmddhh24miss.jpg。\n");printf("tname 数据存放的表名。\n");printf("timetvl 本程序运行的时间间隔,单位:秒。\n");return -1;}CloseIOAndSignal();signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[1],"a+")==false){printf("打开日志文件失败(%s)。\n",argv[1]); return -1;}logfile.Write("程序启动。\n");while (true){// logfile.Write("开始扫描目录。\n");// 扫描数据文件存放的目录,只匹配"PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20*.JPG"if (Dir.OpenDir(argv[3],"PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20*.JPG",1000,true,true)==false){logfile.Write("Dir.OpenDir(%s) failed.\n",argv[3]); sleep(atoi(argv[6])); continue;} while (true) // 逐个处理目录中的数据文件{if (Dir.ReadDir()==false) break;if (_pzhrain24file(argv[2],argv[4],argv[5])==false) {logfile.WriteEx("失败。\n"); continue;}}if (conn.m_state==1) conn.disconnect(); sleep(atoi(argv[6]));}return 0;
}
void EXIT(int sig)
{logfile.Write("程序退出,sig=%d\n\n",sig);exit(0);
}//1111111111111111111111111111111111111111111处理数据文件
bool _pzhrain24file(char *strargv2,char *strargv4,char *strargv5)
{char strddatetime[21]; // 文件的数据时间,格式yyyymmddhh24missmemset(strddatetime,0,sizeof(strddatetime));strncpy(strddatetime,strstr(Dir.m_FileName,"20"),14);//搜索文件名PWCP_TWC…中20,后面取14位,重命名为zhrain24_%s.jpgchar strdstfilename[301]; // 目标文件名,不带路径memset(strdstfilename,0,sizeof(strdstfilename));snprintf(strdstfilename,300,"zhrain24_%s.jpg",strddatetime);char strdstfilepath[301]; // 目标文件存放的目录memset(strdstfilepath,0,sizeof(strdstfilepath));snprintf(strdstfilepath,300,"%s/",strargv4);strncat(strdstfilepath,strddatetime,4); strcat(strdstfilepath,"/"); // 年的子目录strncat(strdstfilepath,strddatetime+4,2); strcat(strdstfilepath,"/"); // 月的子目录strncat(strdstfilepath,strddatetime+6,2); strcat(strdstfilepath,"/"); // 日的子目录char strfulldstfilename[301]; // 目标文件名,全路径memset(strfulldstfilename,0,sizeof(strfulldstfilename));snprintf(strfulldstfilename,300,"%s%s",strdstfilepath,strdstfilename);// 如果文件已处理(目标文件已存在),直接返回成功。if (access(strfulldstfilename,F_OK) == 0) return true;if (conn.m_state==0){if (conn.connecttodb(strargv2,"Simplified Chinese_China.ZHS16GBK")!=0){logfile.Write("connect database(%s) failed.\n%s\n",strargv2,conn.m_cda.message); return false;}// logfile.Write("连接数据库成功。\n");}// 把源文件复制到目标文件if (COPY(Dir.m_FullFileName,strfulldstfilename)==false) {logfile.Write("复制文件COPY(%s,%s)...failed.\n",Dir.m_FullFileName,strfulldstfilename); return false;}// 把非结构化数据文件写入oracle数据库的表中if (FileToTable(&conn,&logfile,strargv5,strfulldstfilename,strddatetime)!=0){logfile.Write("把文件%s存入%s...failed.\n",strfulldstfilename,strargv5); return false;}logfile.Write("把文件%s存入%s...ok.\n",strfulldstfilename,strargv5); return true;
}
10.磁盘/cpu信息收集:内存信息free -m,和top命令查看的内存是一样的,也在系统文件/proc/meminfo
//diskinfo.cpp,写入xml文件中再入库
#include "_public.h"
void EXIT(int sig);
CLogFile logfile;int main(int argc,char *argv[])
{if (argc != 4){printf("\n");printf("Using:./diskinfo hostname logfilename outputpath\n");printf("Example:/htidc/public/bin/diskinfo 118.89.50.198 /tmp/htidc/log/diskinfo.log /tmp/htidc/monclient\n\n");printf("此程序调用df命名,把本服务器的磁盘使用率信息写入xml文件。\n");printf("hostname是本服务器的主机名,为了方便识别,也可以用IP。\n");printf("logfilename是本程序的日志文件名。\n");printf("outputpath是输出的xml文件存放的目录。\n");printf("此程序运行在需要监控的服务器上(本程序只适用Linux系统),采集后的xml文件由文件传输程序发送给数据处理服务程序入库。\n\n\n");return -1;}CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[2],"a+") == false){printf("logfile.Open(%s) failed.\n",argv[2]); return -1;}FILE *fp=0;if ( (fp=popen("df -k --block-size=1M","r")) == NULL ){logfile.Write("popen(df -k --block-size=1M) failed.\n"); return false;}char strXMLFileName[301],strLocalTime[21];memset(strXMLFileName,0,sizeof(strXMLFileName));memset(strLocalTime,0,sizeof(strLocalTime));LocalTime(strLocalTime,"yyyymmddhh24miss");snprintf(strXMLFileName,300,"%s/diskinfo_%s_%s.xml",argv[3],strLocalTime,argv[1]);CFile XMLFile;if (XMLFile.OpenForRename(strXMLFileName,"w+") == false ){logfile.Write("XMLFile.OpenForRename(%s) failed.\n",strXMLFileName); pclose(fp); return -1;}XMLFile.Fprintf("<data>\n");CCmdStr CmdStr;char strBuffer[1024],strLine[500];while (true){memset(strBuffer,0,sizeof(strBuffer));if (FGETS(fp,strBuffer,500) == false) break;// 如果没有找到“%”,就再读取一行,与strBuffer拼起来if (strstr(strBuffer,"%") == 0){memset(strLine,0,sizeof(strLine));if (FGETS(fp,strLine,500) == false) break;strcat(strBuffer," "); strcat(strBuffer,strLine);}// 删除字符串前后的空格和换行符DeleteLRChar(strBuffer,' '); DeleteLRChar(strBuffer,'\n');// 把字符串中间的多个空格全部转换为一个空格UpdateStr(strBuffer," "," ");// 把全内容全部转换为小写ToLower(strBuffer);// 除了磁盘信息,还有可能是内存,SMB等其它文件,都丢弃掉if (strncmp(strBuffer,"/dev",4) != 0) continue;CmdStr.SplitToCmd(strBuffer," ");if (CmdStr.CmdCount() != 6) continue;char strusep[21];memset(strusep,0,sizeof(strusep));strcpy(strusep,CmdStr.m_vCmdStr[4].c_str());UpdateStr(strusep,"%","");char strLocalTime[21];memset(strLocalTime,0,sizeof(strLocalTime));LocalTime(strLocalTime,"yyyymmddhh24miss");XMLFile.Fprintf(\"<nodip>%s</nodip>"\"<crttime>%s</crttime>"\"<filesystem>%s</filesystem>"\"<total>%0.02f</total>"\"<used>%0.02f</used>"\"<available>%0.02f</available>"\"<usep>%0.02f</usep>"\"<mount>%s</mount><endl/>\n",argv[1],strLocalTime,CmdStr.m_vCmdStr[0].c_str(),atof(CmdStr.m_vCmdStr[1].c_str())/1024.0,atof(CmdStr.m_vCmdStr[2].c_str())/1024.0,atof(CmdStr.m_vCmdStr[3].c_str())/1024.0,(atof(CmdStr.m_vCmdStr[2].c_str())/atof(CmdStr.m_vCmdStr[1].c_str()))*100.0,CmdStr.m_vCmdStr[5].c_str());}XMLFile.Fprintf("</data>\n");pclose(fp);XMLFile.CloseAndRename();logfile.Write("create %s ok.\n",strXMLFileName);exit(0);
}void EXIT(int sig)
{if (sig > 0) signal(sig,SIG_IGN);logfile.Write("catching the signal(%d).\n",sig);logfile.Write("diskinfo exit.\n");exit(0);
}
cpuinfo.cpp思路是定义三个结构体变量
,加载cpu信息到结构体里,睡60s,再继续加载cpu信息到结构体里,再将两结构体成员相减,就可以知道一分钟内cpu情况,采用的是一分钟信息。vi /proc/stat如下。
//cpuinfo.cpp
#include "_public.h"
void EXIT(int sig);
CLogFile logfile;
struct st_cpuinfo
{double user;double sys;double wait;double nice;double idle;double irq;double softirq;double total;
};
struct st_cpuinfo stcpuinfo1,stcpuinfo2,stcpuinfo3;
bool LoadCPUInfo(struct st_cpuinfo &stcpuinfo);int main(int argc,char *argv[])
{if (argc != 4){printf("\n");printf("Using:./cpuinfo hostname logfilename outputpath\n");printf("Example:/htidc/public/bin/cpuinfo 118.89.50.198 /tmp/htidc/log/cpuinfo.log /tmp/htidc/monclient\n\n");printf("此程序读取/proc/stat文件,把本服务器的CPU使用率信息写入xml文件。\n");printf("hostname是本服务器的主机名,为了方便识别,也可以用IP。\n");printf("logfilename是本程序的日志文件名。\n");printf("outputpath是输出的xml文件存放的目录。\n");printf("此程序运行在需要监控的服务器上(本程序只适用Linux系统),采集后的xml文件由文件传输程序发送给数据处理服务程序入库。\n\n\n");return -1;}//memset(strHostName,0,sizeof(strHostName));//strncpy(strHostName,argv[2],20);CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[2],"a+") == false){printf("logfile.Open(%s) failed.\n",argv[2]); return -1;}memset(&stcpuinfo1,0,sizeof(struct st_cpuinfo));memset(&stcpuinfo2,0,sizeof(struct st_cpuinfo));memset(&stcpuinfo3,0,sizeof(struct st_cpuinfo));if (LoadCPUInfo(stcpuinfo1) ==false) return -1; sleep(60);if (LoadCPUInfo(stcpuinfo2) ==false) return -1;stcpuinfo3.user=stcpuinfo2.user-stcpuinfo1.user;stcpuinfo3.sys=stcpuinfo2.sys-stcpuinfo1.sys;stcpuinfo3.wait=stcpuinfo2.wait-stcpuinfo1.wait;stcpuinfo3.nice=stcpuinfo2.nice-stcpuinfo1.nice;stcpuinfo3.idle=stcpuinfo2.idle-stcpuinfo1.idle;stcpuinfo3.irq=stcpuinfo2.irq-stcpuinfo1.irq;stcpuinfo3.softirq=stcpuinfo2.softirq-stcpuinfo1.softirq;stcpuinfo3.total=stcpuinfo3.user+stcpuinfo3.sys+stcpuinfo3.wait+stcpuinfo3.nice+stcpuinfo3.idle+stcpuinfo3.irq+stcpuinfo3.softirq;char strLocalTime[21];memset(strLocalTime,0,sizeof(strLocalTime));LocalTime(strLocalTime,"yyyymmddhh24miss");char strXMLFileName[301];memset(strXMLFileName,0,sizeof(strXMLFileName));snprintf(strXMLFileName,300,"%s/cpuinfo_%s_%s.xml",argv[3],strLocalTime,argv[1]);CFile XMLFile;if (XMLFile.OpenForRename(strXMLFileName,"w+") == false ){logfile.Write("XMLFile.OpenForRename(%s) failed.\n",strXMLFileName); return -1;}XMLFile.Fprintf("<data>\n");XMLFile.Fprintf("<nodip>%s</nodip><crttime>%s</crttime><user>%0.02f</user><sys>%0.02f</sys><wait>%0.02f</wait><nice>%0.02f</nice><idle>%0.02f</idle><usep>%0.02f</usep><endl/>\n",argv[1],strLocalTime,stcpuinfo3.user/stcpuinfo3.total*100.0,stcpuinfo3.sys/stcpuinfo3.total*100.0,stcpuinfo3.wait/stcpuinfo3.total*100.0,stcpuinfo3.nice/stcpuinfo3.total*100.0,stcpuinfo3.idle/stcpuinfo3.total*100.0,100.0-stcpuinfo3.nice/stcpuinfo3.total*100.0);XMLFile.Fprintf("</data>\n");XMLFile.CloseAndRename();logfile.Write("create %s ok.\n",strXMLFileName);exit(0);
}
void EXIT(int sig)
{if (sig > 0) signal(sig,SIG_IGN);logfile.Write("catching the signal(%d).\n",sig);logfile.Write("cpuinfo exit.\n");exit(0);
}bool LoadCPUInfo(struct st_cpuinfo &stcpuinfo)
{CFile CPUFile;if (CPUFile.Open("/proc/stat","r") == false ){logfile.Write("CPUFile.OpenForRead(/proc/stat) failed.\n"); return false;}CCmdStr CmdStr;char strBuffer[1024];while (true){memset(strBuffer,0,sizeof(strBuffer));if (CPUFile.FFGETS(strBuffer,500) == false) break;// 删除字符串前后的空格DeleteLRChar(strBuffer,' ');// 把字符串中间的多个空格全部转换为一个空格UpdateStr(strBuffer," "," ");ToLower(strBuffer);CmdStr.SplitToCmd(strBuffer," ");if (strcmp(CmdStr.m_vCmdStr[0].c_str(),"cpu")==0) {stcpuinfo.user=atof(CmdStr.m_vCmdStr[1].c_str());stcpuinfo.sys=atof(CmdStr.m_vCmdStr[2].c_str());stcpuinfo.wait=atof(CmdStr.m_vCmdStr[3].c_str());stcpuinfo.nice=atof(CmdStr.m_vCmdStr[4].c_str());stcpuinfo.idle=atof(CmdStr.m_vCmdStr[5].c_str());stcpuinfo.irq=atof(CmdStr.m_vCmdStr[6].c_str());stcpuinfo.softirq=atof(CmdStr.m_vCmdStr[7].c_str());return true;}}logfile.Write("Read /proc/stat failed.\n"); return false;
}
vi /tmp/htdic/monclient/cpu*,如下是收集到的信息。