给定 9000 个数据行(500 * 您在上面提供的 18 个示例行),以下代码将在大约一秒钟内运行,这应该会达到您的性能目标:
DECLARE
l_xml_data XMLTYPE;
BEGIN
<<baccstart_loop>>
FOR r_baccstart IN (
-- Initially, get all start/end rows
WITH delimiters_all_rows AS (
SELECT d.*
FROM data d
WHERE d.name IN ('BACCSTART', 'BACCEND')
)
-- Lead over the rows order by row_num to get the start/end pairs
-- into a single row
, delimiters_all_rows_joined AS (
SELECT
dar.row_num baccstart_row_num
, LEAD(dar.row_num) OVER (ORDER BY row_num) baccend_row_num
, dar.name
FROM delimiters_all_rows dar
)
-- Eliminate the BACCEND rows as we have their row_num values
SELECT *
FROM delimiters_all_rows_joined darj
WHERE name = 'BACCSTART'
)
LOOP
-- Now get all relevant rows for this data block
WITH data_rows AS (
SELECT d.*
FROM data d
WHERE d.row_num > r_baccstart.baccstart_row_num AND d.row_num < r_baccstart.baccend_row_num
AND (
d.name IN ('TOTAL','ACCNAME')
OR (d.name = 'ABCD' AND d.row_num = (
SELECT MIN(d2.row_num)
FROM data d2
WHERE d2.row_num > r_baccstart.baccstart_row_num AND d2.row_num < r_baccstart.baccend_row_num
AND d2.name = 'ABCD'
))
)
)
-- Agg the rows into a single XML block (note: EVALNAME for dynamic XML element name)
SELECT
XMLELEMENT("BACCSTART"
, XMLAGG(
XMLELEMENT(EVALNAME(dr.name), dr.value)
ORDER BY DECODE(dr.name, 'TOTAL', 1, 'ABCD', 2, 3)
)
) xml_data
INTO l_xml_data
FROM data_rows dr;
INSERT INTO data_xml VALUES (l_xml_data);
END LOOP baccstart_loop;
END;
我试图让它在一个查询中完成所有工作,但是XMLAGG一旦你应用GROUP BY这么多行的 a,性能似乎就会下降(我已经看到一次又一次发生这种情况)。XMLAGG当限制为尽可能少的行时,性能变得更容易接受。
生成的 XML 被插入到一个名为的表中data_xml(这样我可以很容易地看到结果),但是一旦生成了 XML 片段,您就可以用您需要做的任何事情来替换它们!
我建议您是否将数据插入到表中,然后只需执行INSERT/SELECT而不是SELECT INTO后跟 an INSERT,因为由于 SQL 和 PL/SQL 之间缺少上下文切换,它会更快。
以下 DDL/DML 用于设置此示例:
CREATE TABLE data (
row_num INTEGER
, name VARCHAR2(10)
, value VARCHAR2(10)
)
/
CREATE TABLE data_xml (
xml_data XMLTYPE
)
/
CREATE SEQUENCE data_seq
START WITH 1
INCREMENT BY 1
CACHE 1000
/
BEGIN
FOR idx IN 1 .. 500 LOOP
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'DOCSTART', NULL);
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'BACCSTART', NULL);
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'ABCD', 'abcd');
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'ABCD', 'abcd2');
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'PQRS', 'pqrs');
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'PQRS', 'pqrs2');
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'TOTAL', '100');
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'ACCNAME', 'name');
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'BACCEND', NULL);
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'BACCSTART', NULL);
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'ABCD', 'abcd');
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'ABCD', 'abcd2');
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'PQRS', 'pqrs3');
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'PQRS', 'pqrs4');
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'TOTAL', '150');
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'ACCNAME', 'name');
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'BACCEND', NULL);
INSERT INTO data (row_num, name, value)
VALUES (data_seq.NEXTVAL, 'DOCEND', NULL);
END LOOP;
END;
/