最近在做的一个项目中,需要从UDP接收数据,数据记录了同样的点不同时间的值,然后每分钟定时将接收到的值更新到数据库中,但是接收数据的时间虽然在一分钟左右,但时间并不准确,如果UDP没有接收到数据,也必须将上次收到的数据再次提交给数据库,因此将接收数据和定时更新分开,接收数据和提交数据互不干涉。然而这么做问题来了,整个系统是建立在ORACLE库上的,而ORACLE是没有内存表的,如果在ORACLE中建表来保存数据,会对硬盘的频繁读写,时间长了容易出现硬盘坏道;如果在程序中保存,那么提交时必须采用循环整个数组,一次次地提交,效率上就会比较低。
我考虑了两种方案,一种是采用在操作系统上建虚拟硬盘,然后再在虚盘上建立SQL server数据库,接收到的UDP数据直接发送到SQL server更新数据,然后再在ORACLE中建立一个从这个SQL server到ORACLE数据库的透明网关,定时从SQL server库中更新ORACLE库德数据。
另外一种方法,就是使用包和用户数据类型来实现内存表,这样可以利用ORACLE本身的功能,而不用再去使用别的数据库服务,从效率上来说,也比较高。
ORACLE库中表mytable结构为:
MYINDEX NUMBER
SAVETIME DATE
MYVALUE NUMBER
首先,需要建立一个对象类型:
CREATE TYPE myobjtype AS OBJECT ( "MYINDEX " NUMBER, "MYNAME" VARCHAR2(12), "MYVALUE" FLOAT )
然后根据这个对象类型来建立一个表类型:
CREATE TYPE mytabletype AS TABLE OF myobjtype
鉴于UDP接收时更新的需要,再建立一个对象类型用于更新:
CREATE TYPE mysetvalobjtype AS OBJECT ( "MYNAME" VARCHAR2(12), "MYVALUE" FLOAT )
然后建立一个程序包:
CREATE OR REPLACE PACKAGE mytest is
TESTNO mytabletype :=mytabletype ();
Procedure setTESTNO(setval in mysetvalobjtype );
Procedure ADDTESTNO(setval in myobjtype );
function getTESTNO return mytabletype ;
end MYTEST;
建立包体:
CREATE OR REPLACE PACKAGE BODY ."" is
Procedure setTESTNO(setval in TESTNAMEVAL)
is
i number;
begin
for i in 1..TESTNO.count loop
if TESTNO(i).MYNAME=setval.MYNAME then
TESTNO(i).MYVALUE:=setval.MYVALUE;
end if;
end loop;
end;
Procedure ADDTESTNO(setval in myobjtype )
is
begin
TESTNO.extend;
TESTNO(TESTNO.count):=setval;
end;
function getTESTNO return mytabletype
is
begin
return TESTNO;
end;
end MYTEST;
在这当中,TESTNO是保存数据的变量, ADDTESTNO是增加数据中成员的过程,setTESTNO是修改数据中成员的值的过程,getTESTNO是返回所有成员的函数,
然后建一个程序来接收UDP(我使用的是C#),程序在开始向数据库写数据前要初始化,使用ADDTESTNO将变量的成员加进去,下面是初始化循环中的语句:
cmd.CommandText = "mytest.ADDTESTNO(TESTOBJTYPE(" + myindex.ToString() + ",/'" + myname + "/',0))";
cmd.ExecuteNonQuery();
其中myindex是循环中当前的数据对应的编号,myname 是当前的数据名称(UDP以它作为数据的标识)
然后是接收UDP数据后对变量成员的修改:
cmd.CommandText = "mytest.setTESTNO(TESTNAMEVAL(/'" +udpdataname + "/'," +udpdatavalue.ToString() + "))";
cmd.ExecuteNonQuery();
其中udpdataname 是从UDP中解析出来的名称,udpdatavalue是从UDP中解析出来的值。
最后就是每分钟定时修改数据了:
cmd.CommandText = "insert into testmemtable select MYINDEX,sysda,MYVALUE from table(mytest.gettestno)";
cmd.ExecuteNonQuery();
运行程序,程序就会定时将数据保存到数据库。
需要注意的是,写包变量和读包变量要在同一个程序,如果程序退出,变量TESTNO 中的内容也会被清掉,虽然在程序的生命周期中该变量可以当作全局的来使用,但是其它程序无法读取变量中的内容的。