如果在存储过程中使用FOR UPDATE子句,我应该何时“提交”?在关闭打开的光标之后还是在关闭打开的光标之前?下面是我正在使用的程序,我是在正确的方式做吗?
CREATE OR REPLACE PROCEDURE Proc_UpdateCSClientCount(inMerid IN VARCHAR2,
outCliCount OUT NUMBER,
outretvalue OUT NUMBER)
AS
CURSOR c1 IS
SELECT CLIENT_COUNT
FROM OP_TMER_CONF_PARENT
WHERE MER_ID = inMerid
FOR UPDATE OF CLIENT_COUNT;
BEGIN
OPEN c1;
IF SQL%ROWCOUNT = 1 THEN
FETCH c1 INTO outCliCount;
outCliCount := outCliCount + 1;
UPDATE OP_TMER_CONF_PARENT
SET CLIENT_COUNT = outCliCount
WHERE CURRENT OF c1;
END IF;
outretvalue := 0;
CLOSE c1;
COMMIT;
EXCEPTION
WHEN no_data_found THEN
outretvalue := -1;
END;
字符串
3条答案
按热度按时间pxy2qtax1#
您应该在事务结束时提交。我怀疑你能找到一个合理的情况,即事务的结束是在
FOR UPDATE
循环的中间。你可能听说过,频繁地承诺是一件好事。这是一个错误的神话,这是totally wrong。在Oracle中,情况正好相反:提交涉及到额外的工作,因此你应该只在所有工作都完成后才提交,而不是在此之前。
此外,从逻辑的Angular 来看,如果您可以从头开始,而不是完成一半的工作,那么从错误中恢复是难以想象的容易。
国际海事组织认为,在程序中作出承诺应极为罕见。调用应用程序应该进行必要的检查,并最终决定是否提交数据。
总之,你不能在
FOR UPDATE
循环中提交(它会产生一个ORA-01002: fetch out of sequence
),这是一件好事。每当你发现自己在一个正常的循环中提交时,你应该问问自己这个提交是否真的有必要--很可能不是。如果你真的需要提交 * 和 * 只获取一次,那么你是在关闭游标之前还是之后提交都没有关系。
根据代码摘录更新:在你的代码中有很多东西需要纠正(我想它不是直接的生产代码,但仍然是):
SELECT INTO
可以产生NO_DATA_FOUND
。SELECT
,则SQL%ROWCOUNT
为NULL。c1%ROWCOUNT
,但这将只返回获取的行数:0
在初始open
之后。FOR UPDATE NOWAIT
,这样两个会话就不会互相阻塞。如果您只使用FOR UPDATE
,您还不如使用单个UPDATE
,而不是预先使用SELECT
。id
上调用这个函数呢?这可能是调用应用程序/过程中的一个错误,所以你不应该抓住它。所以你可以像这样重写你的过程:
字符串
zaqlnxep2#
从Oracle文档:
所有行在打开光标时被锁定,而不是在读取它们时被锁定。**提交或回滚事务时,行将被解锁。**由于行不再被锁定,提交后无法从FOR UPDATE游标中获取。
这很重要如果你已经完成了任务(完成了抓取),如果你在关闭游标之前或之后提交,这并不重要。
但是如果需要在读取之间提交,作为一种变通方法,使用带有rowid的update,而不是
where current of
。doc示例:字符串
更新(问题编辑后):你可以在一个SQL中做到这一点,而不需要游标。
型
更新2。代码应更新如下:
型
也就是说:应该在计算受影响的行之前进行提取,受影响的行是
c1%rowcount
,而不是sql%rowcount
。如果你想知道一行是否被更新,你应该给if加上一个else,并给outretvalue参数赋一个特殊的值。wwwo4jvm3#
如果在关闭游标之前提交,然后再次尝试获取,则会得到INVALID_CURSOR异常。我建议在关闭游标后提交。