我使用了以下内容(检查循环中的错误,如果它们存在,我将它们插入表中):
FOR rec IN (SELECT MAX(t.s_id) as s_id,t.sdate,t.stype,t.snumber,t.code,
SUM(t.amount) as amount, t... (other fields)
FROM stable t WHERE t.sdate=p_date AND t.stype=p_type
AND t.snumber=p_num
GROUP BY t.sdate,t.snumber,t.stype, t... (other fields)) LOOP
v_reason := null;
BEGIN
SELECT d.source_id INTO i_source_id FROM mapping m, source d
WHERE TO_NUMBER(m.stage)=rec.snumber AND
m.month=EXTRACT(MONTH FROM rec.sdate) AND
m.year=EXTRACT(YEAR FROM rec.sdate) AND m.desc=d.source_desc AND
m.month=d.month AND m.year=d.year AND m.name='SOURCE';
EXCEPTION
WHEN OTHERS
THEN
e_id := 1;
v_reason := 'source_id';
END;
IF (v_reason IS NULL) THEN
BEGIN
SELECT p.product_id INTO i_product_id FROM mapping m, product p
WHERE m.stage=rec.code AND
m.month=EXTRACT(MONTH FROM rec.sdate) AND
m.year=EXTRACT(YEAR FROM rec.sdate) AND
m.desc=p.product_name AND m.month=p.month AND
m.year=p.year AND m.name='PRODUCT';
EXCEPTION
WHEN OTHERS
THEN
e_id := 2;
v_reason := 'product_id';
END;
END IF;
--- and 5 more checks from other tables ---
---....---
IF (v_reason IS NULL) THEN
INSERT INTO tbl_destination(sdate,source_id,product_id,amount, ... and others)
VALUES(rec.sdate,i_source_id,i_product_id,NVL(abs(rec.amount),0), ...);
ELSE
INSERT INTO tbl_errors(rec_id,e_id,desc) VALUES(rec.s_id,e_id,v_reason);
END IF;
COMMIT;
END LOOP;
对于大量记录(约20000)来说太慢了。求你帮帮我。
答案 0 :(得分:4)
在SQL和PLSQL之间来回跳转会带来巨大的开销。在您的情况下,您执行查询,然后对主查询中找到的每个记录执行新查询。由于SQL和PLSQL之间的所有上下文切换以及由于单独的查询更难以优化,这会减慢批次。写一个大查询。优化器可以完成所有的魔术,你只需要一个上下文切换。
执行下一个查询:它返回的每一行都是错误。您只需要读取sourceCount和productCount以查看哪个是问题(或两者)。
要插入错误:
insert into tbl_errors (rec_id, e_id, desc)
select
s_id,
case
when sourceCount <> 1 then 1
when productCount <> 1 then 2
when ...
end as e_id,
case
when sourceCount <> 1 then 'source_id'
when productCount <> 1 then 'product_id'
when ...
end as reason
from
(
SELECT
MAX(t.s_id) as s_id,
t.sdate,t.stype,t.snumber,t.code,
SUM(t.amount) as amount,
(SELECT count(*)
FROM mapping m, source d
WHERE
TO_NUMBER(m.stage)=rec.snumber AND
m.month=EXTRACT(MONTH FROM rec.sdate) AND
m.year=EXTRACT(YEAR FROM rec.sdate) AND m.desc=d.source_desc AND
m.month=d.month AND m.year=d.year AND m.name='SOURCE') as sourceCount,
(SELECT count(*)
FROM mapping m, product p
WHERE
m.stage=rec.code AND
m.month=EXTRACT(MONTH FROM rec.sdate) AND
m.year=EXTRACT(YEAR FROM rec.sdate) AND
m.desc=p.product_name AND m.month=p.month AND
m.year=p.year AND m.name='PRODUCT') as productCount,
/* other checks */
FROM
stable t
WHERE
t.sdate=p_date AND t.stype=p_type
AND t.snumber=p_num
GROUP BY
t.sdate, t.snumber, t.stype
) x
having
sourceCount <> 1 or productCount <> 1 or /* other checks */
插入好的记录。对检查使用相同的查询,但添加额外的子查询以获取正确的产品ID和源ID。
insert into tbl_destination(sdate,source_id,product_id,amount, ...)
select
sdate,
source_id,
product_id,
amount,
...
from
(
SELECT
MAX(t.s_id) as s_id,
t.sdate,t.stype,t.snumber,t.code,
SUM(t.amount) as amount,
(SELECT count(*)
FROM mapping m, source d
WHERE
TO_NUMBER(m.stage)=rec.snumber AND
m.month=EXTRACT(MONTH FROM rec.sdate) AND
m.year=EXTRACT(YEAR FROM rec.sdate) AND m.desc=d.source_desc AND
m.month=d.month AND m.year=d.year AND m.name='SOURCE') as sourceCount,
(SELECT min(source_id)
FROM mapping m, source d
WHERE
TO_NUMBER(m.stage)=rec.snumber AND
m.month=EXTRACT(MONTH FROM rec.sdate) AND
m.year=EXTRACT(YEAR FROM rec.sdate) AND m.desc=d.source_desc AND
m.month=d.month AND m.year=d.year AND m.name='SOURCE') as source_id,
(SELECT count(*)
FROM mapping m, product p
WHERE
m.stage=rec.code AND
m.month=EXTRACT(MONTH FROM rec.sdate) AND
m.year=EXTRACT(YEAR FROM rec.sdate) AND
m.desc=p.product_name AND m.month=p.month AND
m.year=p.year AND m.name='PRODUCT') as productCount,
(SELECT min(product_id)
FROM mapping m, product p
WHERE
m.stage=rec.code AND
m.month=EXTRACT(MONTH FROM rec.sdate) AND
m.year=EXTRACT(YEAR FROM rec.sdate) AND
m.desc=p.product_name AND m.month=p.month AND
m.year=p.year AND m.name='PRODUCT') as product_id,
/* other checks */
FROM
stable t
WHERE
t.sdate=p_date AND t.stype=p_type
AND t.snumber=p_num
GROUP BY
t.sdate, t.snumber, t.stype
) x
having
sourceCount = 1 and productCount = 1 and /* other checks */
答案 1 :(得分:2)
通常最高效的方法是将plsql转换为基于集合的操作,并摆脱LOOP,我将首先采用驱动查询并将其嵌入到每个查询中(在循环中)。然后将它们变成插入物。注意将IF语句中的任何逻辑合并到WHERE子句中。
e.g: 当您在没有找到记录的情况下插入错误时,可以将第一个SELECT INTO .... EXCEPTION块更改为直接插入,其中无法在映射表中找到任何行
INSERT INTO tbl_errors
SELECT s_id, 1 as e_id , 'source_id' as reason
FROM
(
SELECT MAX(t.s_id) as s_id,t.sdate,t.stype,t.snumber,t.code,
SUM(t.amount) as amount, t... (other fields)
FROM stable t
WHERE t.sdate=p_date AND t.stype=p_type AND t.snumber=p_num
GROUP BY t.sdate,t.snumber,t.stype, t... (other fields)
) drv
LEFT JOIN mapping m ON TO_NUMBER(m.stage) = drv.s_id --etc
LEFT JOIN source d ON m.desc=d.source_desc AND m.month=d.month --etc
WHERE m.stage IS NULL
最终你会得到几个插入,现在应该可以优化furthur并将所有选择合并到一个语句中并将操作作为单个插入执行。
然后插入错误只需插入驱动查询中没有错误的行
即:
INSERT INTO tbl_destination
SELECT * from drv
WHERE NOT EXISTS(SELECT * from tbl_errors WHERE s_id=drv.s_id)