我制作了一个游标,以便合并/更新数据库中的记录
我想知道它是否正确或是否有任何建议以改进查询。
DECLARE
CURSOR c_itemloc
IS
SELECT
item ,
loc ,
loc_type ,
source_method ,
primary_supp ,
source_wh
FROM
(SELECT dc_vert.item ,
dc_vert.loc ,
dc_vert.loc_type ,
dc_vert.source_method ,
dc_vert.primary_supp ,
w.primary_vwh source_wh --,dc_vert.source_wh
,
dc_vert.actie ,
MAX(dc_vert.actie) over (PARTITION BY dc_vert.item, dc_vert.loc) actie_max ,
COUNT(dc_vert.primary_supp) over (PARTITION BY dc_vert.item, dc_vert.loc) primary_count
FROM dc_item_loc_pim_lms dc_vert ,
item_supplier isu ,
store sto ,
wh w
WHERE dc_vert.primary_supp IS NOT NULL
AND isu.item = dc_vert.item
AND dc_vert.primary_supp = isu.supplier
AND W.WH = dc_vert.source_wh
AND sto.store = dc_vert.loc
AND ISU.SUPP_DISCONTINUE_DATE >= SYSDATE
)
WHERE actie = actie_max
AND primary_count = 1;
l_item item_loc.item%TYPE;
l_loc item_loc.loc%TYPE;
loc_type item_loc.loc_type%TYPE;
l_source_method item_loc.source_method%TYPE;
l_primary_supp item_loc.primary_supp%TYPE;
l_source_wh item_loc.source_wh%TYPE;
i NUMBER;
l_commit VARCHAR2(1) := 'Y';
BEGIN
i :=0;
FOR r_itemloc IN c_itemloc
LOOP
i := i+1;
UPDATE item_loc il
SET
il.source_method = r_itemloc.source_method , -- 'S'
il.loc_type = r_itemloc.loc_type , -- 'S'
il.primary_supp = r_itemloc.primary_supp ,
il.source_wh = r_itemloc.source_wh ,
il.last_update_datetime = SYSDATE
WHERE item = r_itemloc.item
AND loc = r_itemloc.loc;
IF l_commit = 'Y' AND mod(i, 1000) = 0 THEN
COMMIT ;
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('SOMETHING WENT WRONG');
END;
答案 0 :(得分:7)
假设代码在功能上符合您的要求......
1)删除异常处理程序。捕获一个你无法处理的未知异常没有任何好处,除非你正在做一些事情,比如记录它并重新抛出它。仅捕获异常以调用dbms_output
不仅隐藏了调试异常所需的异常详细信息和堆栈跟踪,如果调用方不能从缓冲区中读取{{1},它可能会完全隐藏异常写入。
2)在循环中提交通常是一个坏主意。最重要的是,如果你的会话在中间死亡会发生什么?您有一个部分提交的更新,以后在以后重新启动代码时无法恢复操作。您必须重新更新之前更新的所有行以及您提交的可能具有下游影响的更新。并且在循环中提交会减慢代码的速度。
3)如果您要做的只是更新表,最有效的方法是编写一个dbms_output
语句,一次更新所有行,而不是迭代光标并做很多事情单行更新。可能有其他理由支持游标(例如,它可能使代码更容易让其他开发人员理解),但从性能的角度来看,如果你能在SQL中做到这一点,那么SQL将是最有效的方法。 / p>
答案 1 :(得分:4)
1)您正在用无意义的错误替换有意义的错误。另外,如果您还没有set output on
,则会完全错过错误。最好的办法是简单地删除异常块。如果您不能这样做,则应至少将SQLERRM
和DBMS_UTILITY.FORMAT_ERROR_BACKTRACE
转储到DBMS_OUTPUT
。要很好地捕获和记录错误,您需要将异常详细信息传递给一个单独的过程,该过程将使用自治事务将这些详细信息写入表中。虽然,即使在这种情况下,你最好在录制之后重新提出错误。
2)在大多数情况下,提交每个X记录都是不好的做法。如果这些记录都在一起更新,那么它们应该是同一事务的一部分。
3)您可以使用UPDATE
或MERGE
在一个语句中执行此操作。通常这是首选,因为它避免了额外的上下文切换。就个人而言,在这种情况下我喜欢MERGE
:
MERGE INTO item_loc il
USING (SELECT item,
loc,
loc_type,
source_method,
primary_supp,
source_wh
FROM (SELECT dc_vert.item,
dc_vert.loc,
dc_vert.loc_type,
dc_vert.source_method,
dc_vert.primary_supp,
w.primary_vwh source_wh,
dc_vert.actie,
MAX(dc_vert.actie) OVER (PARTITION BY dc_vert.item, dc_vert.loc) actie_max,
COUNT(dc_vert.primary_supp) OVER (PARTITION BY dc_vert.item, dc_vert.loc) primary_count
FROM dc_item_loc_pim_lms dc_vert,
item_supplier isu,
store sto,
wh w
WHERE dc_vert.primary_supp IS NOT NULL
AND isu.item = dc_vert.item
AND dc_vert.primary_supp = isu.supplier
AND w.wh = dc_vert.source_wh
AND sto.store = dc_vert.loc
AND isu.supp_discontinue_date >= SYSDATE)
WHERE actie = actie_max AND primary_count = 1) itemloc
ON (il.item = itemloc.item AND il.loc = itemloc.loc)
WHEN MATCHED THEN
UPDATE SET
il.source_method = itemloc.source_method,
il.loc_type = itemloc.loc_type,
il.primary_supp = itemloc.primary_supp,
il.source_wh = itemloc.source_wh,
il.last_update_datetime = SYSDATE;
答案 2 :(得分:0)
我建议你使用现代连接语法
FROM
dc_item_loc_pim_lms dc_vert
INNER JOIN item_supplier isu
ON dc_vert.item = isu.item AND
dc_vert.primary_supp = isu.supplier
INNER JOIN store sto
ON dc_vert.loc = sto.store
INNER JOIN wh w
ON dc_vert.source_wh = w.wh
WHERE
dc_vert.primary_supp IS NOT NULL AND
isu.supp_discontinue_date >= SYSDATE