Oracle 插入到包含200列的表中
我们的Oracle DB中有一个表,有200列,我们必须插入其中,我们有一个C程序,在该表中插入请求,它使用Pro*C调用Oracle DB中的存储程序来完成这项工作 目前,我们使用TLV格式{Tag Length Value}序列化所有字段,并且有一个带有Varchar2参数的函数(字段的序列化列表)。例如:Oracle 插入到包含200列的表中,oracle,serialization,plsql,insert,oracle-pro-c,Oracle,Serialization,Plsql,Insert,Oracle Pro C,我们的Oracle DB中有一个表,有200列,我们必须插入其中,我们有一个C程序,在该表中插入请求,它使用Pro*C调用Oracle DB中的存储程序来完成这项工作 目前,我们使用TLV格式{Tag Length Value}序列化所有字段,并且有一个带有Varchar2参数的函数(字段的序列化列表)。例如: 008603701212345678901201100611111104800622222220000633333320100644444401201420200321164712 更
008603701212345678901201100611111104800622222220000633333320100644444401201420200321164712
更具可读性:
0086 037 012 123456789012 011 006 111111 048 006 222222 200 006 333333 201 006 444444 012 014 20200321164712
这意味着:
String Len:86
Field037 with len 12:123456789012
Field011 with len 6:111111
Field048 with len 6:222222
Field200 with len 6:333333
Field201 with len 6:444444
Field012 with len 14:20200321164712
这些字段中的每一个都映射到表中的一个字段,存储程序解析这个巨大的字符串并将其填充到一个rowtype中,最后在表中插入该行
多年来,它一直工作得很好,但当我们准备好处理更多的请求(从而插入更多的内容)时,我们的DBA说我们正在使用大量的CPU来反序列化TLV。所以我们必须改变我们的插入方法
我目前在C中创建了一个结构,映射其中的所有字段并调用:
typedef struct
{
char field37[12+1];
char field11[6+1];
char field48[6+1];
char field200[6+1];
char field201[6+1];
char field12[14+1];
} NewRecord;
NewRecord newRecord;
TlvToStruct(sTlv, &newRecord);//Mapper Function
EXEC SQL INSERT INTO MY_TABLE(FIELD37, FIELD11, FIELD200, FIELD201, FIELD12) VALUES(:newRecord);
EXEC SQL COMMIT WORK RELEASE;
这种方法目前运行良好,但我的问题是:我是否应该继续开发并将所有200个字段添加到此结构中,并使用这种方法,还是最好使用PLSQL调用(可能开发并使用另一个插入函数)而不是此SQL插入
我目前了解到PLSQL的好处,但这里存在一些问题:
1- Pro*C does not support PLSQL Records
我试图在这里描述问题,如果不清楚,请询问
谢谢
==================================================
FUNCTION PUT_TAG(P_TAG_NAME IN VARCHAR2, P_TAG_VALUE IN VARCHAR2, P_TLV_STRG IN OUT NOCOPY VARCHAR2)
RETURN PLS_INTEGER IS
V_COUNTER_LOOP PLS_INTEGER := 0;
TMP_VARCHAR VARCHAR2(4096);
BEGIN
P_TLV_STRG := SUBSTR(P_TLV_STRG, 5);
V_COUNTER_LOOP := 1;
WHILE V_COUNTER_LOOP < LENGTH(P_TLV_STRG) LOOP
IF SUBSTR(P_TLV_STRG, V_COUNTER_LOOP, 3) = SUBSTR(P_TAG_NAME, 1, 3) THEN
TMP_VARCHAR :=
SUBSTR(P_TLV_STRG, 1, V_COUNTER_LOOP - 1)
|| SUBSTR(P_TAG_NAME, 1, 3)
|| TO_CHAR(NVL(LENGTH(P_TAG_VALUE), 0), 'FM000')
|| P_TAG_VALUE
|| SUBSTR(P_TLV_STRG, V_COUNTER_LOOP + 6 + TO_NUMBER(SUBSTR(P_TLV_STRG, V_COUNTER_LOOP + 3, 3)));
P_TLV_STRG := TO_CHAR(LENGTH(TMP_VARCHAR), 'FM0000') || TMP_VARCHAR;
RETURN (DECLARATION_CST.OK);
END IF;
V_COUNTER_LOOP := V_COUNTER_LOOP + 6 + TO_NUMBER(SUBSTR(P_TLV_STRG, V_COUNTER_LOOP + 3, 3));
END LOOP;
P_TLV_STRG :=
P_TLV_STRG
|| SUBSTR(P_TAG_NAME, 1, 3)
|| TO_CHAR(NVL(LENGTH(P_TAG_VALUE), 0), 'FM000')
|| P_TAG_VALUE;
P_TLV_STRG := TO_CHAR(LENGTH(P_TLV_STRG), 'FM0000') || P_TLV_STRG;
RETURN 0;
EXCEPTION
WHEN OTHERS THEN
RETURN -1;
END PUT_TAG;
编辑:
这是我们以前的Get_标记和Put_标记函数:
FUNCTION GET_TAG(P_TAG_NAME IN VARCHAR2, P_TLV_STRG IN VARCHAR2, P_TAG_LEN OUT NOCOPY PLS_INTEGER, P_TAG_VALUE OUT NOCOPY VARCHAR2)
RETURN PLS_INTEGER IS
V_COUNTER_LOOP PLS_INTEGER := 1;
V_TLV_STRG VARCHAR2(4096) := SUBSTR(P_TLV_STRG, 5);
BEGIN
P_TAG_VALUE := NULL;
P_TAG_LEN := 0;
WHILE V_COUNTER_LOOP < LENGTH(V_TLV_STRG) LOOP
IF SUBSTR(V_TLV_STRG, V_COUNTER_LOOP, 3) = P_TAG_NAME THEN
P_TAG_LEN := TO_NUMBER(SUBSTR(V_TLV_STRG, V_COUNTER_LOOP + 3, 3));
P_TAG_VALUE := SUBSTR(V_TLV_STRG, V_COUNTER_LOOP + 6, P_TAG_LEN);
RETURN (DECLARATION_CST.OK);
END IF;
V_COUNTER_LOOP := V_COUNTER_LOOP + 6 + TO_NUMBER(SUBSTR(V_TLV_STRG, V_COUNTER_LOOP + 3, 3));
END LOOP;
RETURN 0;
EXCEPTION
WHEN OTHERS THEN
RETURN -1;
END GET_TAG;
函数GET_TAG(VARCHAR2中的P_TAG_NAME,VARCHAR2中的P_TLV_STRG,P_TAG_LEN OUT NOCOPY PLS_INTEGER,P_TAG_VALUE OUT NOCOPY VARCHAR2)
返回PLS\u整数为
V_计数器循环PLS_整数:=1;
V_TLV_STRG VARCHAR2(4096):=SUBSTR(P_TLV_STRG,5);
开始
P_标签_值:=NULL;
P_TAG_LEN:=0;
而V_计数器_循环<长度(V_TLV_STRG)循环
如果SUBSTR(V_TLV_STRG,V_COUNTER_LOOP,3)=P_TAG_NAME,则
P_标签长度:=至编号(SUBSTR(V_TLV STRG,V_计数器循环+3,3));
P_标记值:=SUBSTR(V_TLV STRG,V_计数器循环+6,P_标记长度);
返回(声明_CST.OK);
如果结束;
V_计数器循环:=V_计数器循环+6+到循环编号(SUBSTR(V_TLV STRG,V_计数器循环+3,3));
端环;
返回0;
例外情况
当其他人
返回-1;
结束获取标签;
===========================================================
FUNCTION PUT_TAG(P_TAG_NAME IN VARCHAR2, P_TAG_VALUE IN VARCHAR2, P_TLV_STRG IN OUT NOCOPY VARCHAR2)
RETURN PLS_INTEGER IS
V_COUNTER_LOOP PLS_INTEGER := 0;
TMP_VARCHAR VARCHAR2(4096);
BEGIN
P_TLV_STRG := SUBSTR(P_TLV_STRG, 5);
V_COUNTER_LOOP := 1;
WHILE V_COUNTER_LOOP < LENGTH(P_TLV_STRG) LOOP
IF SUBSTR(P_TLV_STRG, V_COUNTER_LOOP, 3) = SUBSTR(P_TAG_NAME, 1, 3) THEN
TMP_VARCHAR :=
SUBSTR(P_TLV_STRG, 1, V_COUNTER_LOOP - 1)
|| SUBSTR(P_TAG_NAME, 1, 3)
|| TO_CHAR(NVL(LENGTH(P_TAG_VALUE), 0), 'FM000')
|| P_TAG_VALUE
|| SUBSTR(P_TLV_STRG, V_COUNTER_LOOP + 6 + TO_NUMBER(SUBSTR(P_TLV_STRG, V_COUNTER_LOOP + 3, 3)));
P_TLV_STRG := TO_CHAR(LENGTH(TMP_VARCHAR), 'FM0000') || TMP_VARCHAR;
RETURN (DECLARATION_CST.OK);
END IF;
V_COUNTER_LOOP := V_COUNTER_LOOP + 6 + TO_NUMBER(SUBSTR(P_TLV_STRG, V_COUNTER_LOOP + 3, 3));
END LOOP;
P_TLV_STRG :=
P_TLV_STRG
|| SUBSTR(P_TAG_NAME, 1, 3)
|| TO_CHAR(NVL(LENGTH(P_TAG_VALUE), 0), 'FM000')
|| P_TAG_VALUE;
P_TLV_STRG := TO_CHAR(LENGTH(P_TLV_STRG), 'FM0000') || P_TLV_STRG;
RETURN 0;
EXCEPTION
WHEN OTHERS THEN
RETURN -1;
END PUT_TAG;
函数PUT_TAG(VARCHAR2中的P_TAG_名称,VARCHAR2中的P_TAG_值,P_TLV_STRG IN OUT NOCOPY VARCHAR2)
返回PLS\u整数为
V_计数器循环PLS_整数:=0;
TMP_VARCHAR VARCHAR2(4096);
开始
P_TLV_STRG:=SUBSTR(P_TLV_STRG,5);
V_计数器_循环:=1;
而V_计数器_循环<长度(P_TLV_STRG)循环
如果SUBSTR(P_TLV_STRG,V_COUNTER_LOOP,3)=SUBSTR(P_TAG_NAME,1,3),则
TMP_VARCHAR:=
SUBSTR(P_TLV_STRG,1,V_计数器_循环-1)
||SUBSTR(P_标签_名称,1,3)
||TO_CHAR(NVL(长度(P_标记值),0),'FM000')
||P_标记_值
||SUBSTR(P_TLV_STRG,V_计数器_循环+6+到_编号)(SUBSTR(P_TLV_STRG,V_计数器_循环+3,3));
P_TLV_STRG:=TO_CHAR(长度(TMP_VARCHAR),'FM0000')| TMP_VARCHAR;
返回(声明_CST.OK);
如果结束;
V_计数器循环:=V_计数器循环+6+到循环编号(SUBSTR(P_TLV STRG,V_计数器循环+3,3));
端环;
P_TLV_STRG:=
P_TLV_STRG
||SUBSTR(P_标签_名称,1,3)
||TO_CHAR(NVL(长度(P_标记值),0),'FM000')
||P_标签_值;
P_TLV_STRG:=TO_CHAR(长度(P_TLV_STRG),'FM0000')| P_TLV_STRG;
返回0;
例外情况
当其他人
返回-1;
末端放置标签;
您需要首先在数据库中创建映射表,映射到字段及其长度(在我的示例中,我使用了CTE
作为映射表:您的映射表
)
Oracle安装程序:
SQL> CREATE TABLE TEST1234 (
2 FIELD001 VARCHAR2(4000),
3 FIELD002 VARCHAR2(4000),
4 FIELD003 VARCHAR2(4000),
5 FIELD004 VARCHAR2(4000)
6 );
Table created.
SQL> INSERT INTO TEST1234
2 WITH YOUR_MAPPING_TABLE (FIELD_ID, LEN)
3 AS (
4 SELECT 'FIELD001', 4 FROM DUAL UNION ALL
5 SELECT 'FIELD002', 3 FROM DUAL UNION ALL
6 SELECT 'FIELD003', 3 FROM DUAL UNION ALL
7 SELECT 'FIELD004', 12 FROM DUAL
8 )
9 SELECT * FROM
10 ( SELECT M.FIELD_ID, -- Your string will go in following substring
11 SUBSTR('0086037012123456789012', M.STARTPOS + 1, M.TOTALLEN) AS FIELDVALUE
12 FROM ( SELECT FIELD_ID,
13 SUM(LEN) OVER(ORDER BY FIELD_ID ) - LEN AS STARTPOS,
14 LEN AS TOTALLEN
15 FROM YOUR_MAPPING_TABLE
16 ) M
17 ) PIVOT (
18 MIN ( FIELDVALUE )
19 FOR FIELD_ID IN ( 'FIELD001', 'FIELD002', 'FIELD003', 'FIELD004' )
20 );
1 row created.
解决方案查询:
SQL> CREATE TABLE TEST1234 (
2 FIELD001 VARCHAR2(4000),
3 FIELD002 VARCHAR2(4000),
4 FIELD003 VARCHAR2(4000),
5 FIELD004 VARCHAR2(4000)
6 );
Table created.
SQL> INSERT INTO TEST1234
2 WITH YOUR_MAPPING_TABLE (FIELD_ID, LEN)
3 AS (
4 SELECT 'FIELD001', 4 FROM DUAL UNION ALL
5 SELECT 'FIELD002', 3 FROM DUAL UNION ALL
6 SELECT 'FIELD003', 3 FROM DUAL UNION ALL
7 SELECT 'FIELD004', 12 FROM DUAL
8 )
9 SELECT * FROM
10 ( SELECT M.FIELD_ID, -- Your string will go in following substring
11 SUBSTR('0086037012123456789012', M.STARTPOS + 1, M.TOTALLEN) AS FIELDVALUE
12 FROM ( SELECT FIELD_ID,
13 SUM(LEN) OVER(ORDER BY FIELD_ID ) - LEN AS STARTPOS,
14 LEN AS TOTALLEN
15 FROM YOUR_MAPPING_TABLE
16 ) M
17 ) PIVOT (
18 MIN ( FIELDVALUE )
19 FOR FIELD_ID IN ( 'FIELD001', 'FIELD002', 'FIELD003', 'FIELD004' )
20 );
1 row created.
测试结果
SQL> SELECT * FROM TEST1234;
FIELD001 | FIELD002 | FIELD003 | FIELD004
---------- | ---------- | ---------- | -------------
0086 | 037 | 012 | 123456789012
SQL>
您可以根据逻辑在查询中传递大字符串 谢谢你的解决方案,我只想让字段分离的东西离开数据库,但是你的答案很好,我会测试它并报告性能统计数据“我只想让字段分离的东西离开数据库”。为什么?高性能的基本规则之一是让数据库本身尽可能多地完成工作。不要把数据库仅仅当作数据存储介质。充分利用C程序的所有处理能力。@EdStevens C程序在一个伟大的服务器上,有700个工作线程,在我们的测试中,oracle反序列化所消耗的cpu比整个C程序多得多,这不是optimumHi@Tejash我们的旧解析器也使用了子字符串,感谢您的解决方案,但问题仍然存在每个记录的长度不同?您可以发布PL/SQL存储程序吗?可能是一种优化。什么是Oracle数据库版本?是的,某些字段具有可变长度,是否要查看解析器功能?是的,如果可以共享