文章目录
- 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" ]; then
if [ $SHELL = "/bin/ksh" ]; then
ulimit -p 16384
ulimit -n 65536
else
ulimit -u 16384 -n 65536
fi
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 = -lclntsh
CFLAGS = -g -Wno-write-strings -Wno-unused-variable
all: createtable inserttable selecttable updatetable deletetable execplsql
createtable:createtable.cpp _ooci.h _ooci.cpp
g++ $(CFLAGS) -o createtable createtable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cpp
inserttable:inserttable.cpp _ooci.h _ooci.cpp
g++ $(CFLAGS) -o inserttable inserttable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cpp
selecttable:selecttable.cpp _ooci.h _ooci.cpp
g++ $(CFLAGS) -o selecttable selecttable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cpp
updatetable:updatetable.cpp _ooci.h _ooci.cpp
g++ $(CFLAGS) -o updatetable updatetable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cpp
deletetable:deletetable.cpp _ooci.h _ooci.cpp
g++ $(CFLAGS) -o deletetable deletetable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cpp
execplsql:execplsql.cpp _ooci.h _ooci.cpp
g++ $(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无小数的number
char name[31]; // 商品名称,用char对应oracle的varchar2,注意,表中字段的长度是30,char定义的长度是31,要留C语言的结束符
double sal; // 商品价格,用double数据类型对应oracle有小数的number
char 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对应上面:1
stmt.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无小数的number
char name[31]; // 商品名称,用char对应oracle的varchar2,注意,表中字段的长度是30,char定义的长度是31,要留C语言的结束符
double sal; // 商品价格,用double数据类型对应oracle有小数的number
char 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对应上面:1
stmt.bindin(2,&iminid); //2对应上面:2
stmt.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到5
iminid=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/s
int r; // 降雨量:0.1mm
int 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/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;
}
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/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;
}
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); //数据时间:格式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;
}
long CSIGNALLOG::InsertTable() //把vsignallog容器中的值更新到T_SIGNALDATA表中
{ //可能会返回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)
{
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-成功。
//11111111111111111111111111111111111111111111111111111111111111111
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;
}
//111111111111111111111111111111111111111111111111111111111111111
stmt.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和fieldlen
SplitFields();
// 判断fieldname和fieldlen中元素的个数一定要相同
if (vfieldname.size() != vfieldlen.size() ) { logfile.Write("fieldname和fieldlen的元素个数不同。.\n"); return false; }
return true;
}
//111111111111111111111111111111111111111本程序的业务流程主函数
bool _exptables()
{
// 从系统参数T_SYSARG表中加载已导出数据的最大的keyid
if (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 //全量导出不需要keyid
sprintf(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");
//111111111111111111111111111111111111111111111111111111111111
if (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表中已导出数据的最大的keyid
if (UptMaxKeyid()==false) { logfile.Write("UptMaxKeyid() failed.\n"); return false; }
logfile.Write("create file %s ok.\n",strFileName);
}
}
//1111111111111111111111111111111111111111111111111111111111
if (File.IsOpened()==true) //不够1000条的写入一个文件
{
File.Fprintf("</data>\n");
if (File.CloseAndRename()==false)
{
logfile.Write("File.CloseAndRename(%s) failed.\n",strFileName); return false;
}
// 更新系统参数T_SYSARG表中已导出数据的最大的keyid
if (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一开始为0
if (ifieldlen>maxfieldlen) maxfieldlen=ifieldlen; //得到fieldlen的最大值maxfieldlen
vfieldlen.push_back(ifieldlen);
}
}
//111111111111111111111111111111111从系统参数T_SYSARG表中加载已导出数据的最大的keyid
bool LoadMaxKeyid() //实现增量采集必须把每次导出数据的keyid保存起来,所以采用一个T_SYSARG参数表
{ //taskname作为参数代码argcode
if (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,0
stmt.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]; // 文件的数据时间,格式yyyymmddhh24miss
memset(strddatetime,0,sizeof(strddatetime));
strncpy(strddatetime,strstr(Dir.m_FileName,"20"),14);
//搜索文件名PWCP_TWC…中20,后面取14位,重命名为zhrain24_%s.jpg
char 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*,如下是收集到的信息。