我目前正在使用以下更新或插入 Oracle 语句:
BEGIN
UPDATE DSMS
SET SURNAME = :SURNAME
WHERE DSM = :DSM;
IF (SQL%ROWCOUNT = 0) THEN
INSERT INTO DSMS
(DSM, SURNAME)
VALUES
(:DSM, :SURNAME);
END IF;
END;
这运行良好,只是如果数据与提供的参数值相同,更新语句将执行虚拟更新。我不介意在正常情况下进行虚拟更新,但是在此表上构建了一个复制/同步系统,使用表上的触发器来捕获更新的记录并为许多记录频繁执行此语句仅意味着我会在触发器中造成巨大的流量和同步系统。
是否有任何简单的方法可以重新编写此代码,如果没有必要,更新语句不会更新记录,而无需使用以下 IF-EXISTS 检查代码,我发现这些代码不够流畅,也可能对这项任务不是最有效的?
DECLARE
CNT NUMBER;
BEGIN
SELECT COUNT(1) INTO CNT FROM DSMS WHERE DSM = :DSM;
IF SQL%FOUND THEN
UPDATE DSMS
SET SURNAME = :SURNAME
WHERE DSM = :DSM
AND SURNAME != :SURNAME;
ELSE
INSERT INTO DSMS
(DSM, SURNAME)
VALUES
(:DSM, :SURNAME);
END IF;
END;
我也尝试使用 MERGE INTO 语句,但是当值未修改时它不适用于更新(更新不修改任何内容并执行插入,但发生 PK 冲突)。
完整的 MERGE INTO 示例:
CREATE TABLE DSMS(
dsm VARCHAR2(10) NOT NULL PRIMARY KEY,
surname VARCHAR2(10) NOT NULL
);
> Table created
-- :DSM = 'xx', :SURNAME = 'xx'
MERGE INTO DSMS D
USING (SELECT :DSM AS DSM,
:SURNAME AS SURNAME
FROM DUAL) V
ON (D.DSM = V.DSM)
WHEN MATCHED THEN
UPDATE
SET SURNAME = V.SURNAME
WHERE D.SURNAME <> V.SURNAME
WHEN NOT MATCHED THEN
INSERT (DSM, SURNAME)
VALUES (V.DSM, V.SURNAME);
> Ok - record inserted
-- :DSM = 'xx', :SURNAME = 'xx'
MERGE INTO DSMS D
USING (SELECT :DSM AS DSM,
:SURNAME AS SURNAME
FROM DUAL) V
ON (D.DSM = V.DSM)
WHEN MATCHED THEN
UPDATE
SET SURNAME = V.SURNAME
WHERE D.SURNAME <> V.SURNAME
WHEN NOT MATCHED THEN
INSERT (DSM, SURNAME)
VALUES (V.DSM, V.SURNAME);
> ORA-00001 - Unique constraint violated (PK violation)
看起来 Oracle 在内部使用 UPDATE...IF SQL%ROWCOUNT=0 THEN INSERT... 用于 MERGE INTO 子句?第二个 MERGE INTO 语句失败,因为 update 没有更新任何内容,因此执行了 INSERT 导致 PK 冲突,因为行已经存在,只是值没有改变。