Oracle - UPSERT con aggiornamento non eseguito per valori non modificati
-
04-10-2019 - |
Domanda
sto usando seguente aggiornamento o inserimento dichiarazione Oracle in questo momento:
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;
Questo funziona bene, tranne che le esegue un'istruzione UPDATE fittizi aggiornamento se i dati sono uguali ai valori dei parametri forniti. Non mi dispiacerebbe l'aggiornamento fittizio in situazione normale, ma c'è un sistema di compilazione replica / sincronizzazione su questo tavolo utilizzando i trigger sulle tabelle per catturare aggiornato i record e l'esecuzione di questa dichiarazione di frequente per molti record significa semplicemente che avrei causo traffico enorme nei trigger e il sistema di sincronizzazione.
Esiste un metodo semplice come riformulare questo codice che la dichiarazione di aggiornamento non sarebbe aggiornare il record, se non necessario, senza l'utilizzo di seguito IF-ESISTE codice di controllo che trovo abbastanza per non elegante e forse anche non più efficiente per questo compito?
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;
Ho anche provato ad utilizzare dichiarazione si fondono in, ma non funziona per gli aggiornamenti quando il valore non viene modificato (aggiornamento non modifica nulla e viene eseguito inserto, ma si verifica la violazione PK).
Unisci completa di esempio in:
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)
Sembra che Oracle sta usando UPDATE ... se SQL% ROWCOUNT = 0 quindi inserire ... internamente per fondersi in clausola? Il secondo si fondono in istruzione ha esito negativo, perché l'aggiornamento non aggiorna nulla e quindi INSERT viene eseguita che si traduce in violazione PK, perché riga esiste già solo i valori non sono cambiati.
Soluzione
MERGE
INTO dsms d
USING (
SELECT :DSM AS dsm, :SURNAME AS surname, :FIRSTNAME AS firstname, :VALID AS valud
FROM dual
) v
ON (d.dsm = q.dsm)
WHEN MATCHED THEN
UPDATE
SET SURNAME = v.SURNAME, FIRSTNAME = v.FIRSTNAME, VALID = v.VALID
WHERE d.surname <> v.surname
OR d.firstname <> v.firstname
OR d.valid <> v.valid
WHEN NOT MATCHED THEN
INSERT
INTO (SURNAME, FIRSTNAME, VALID)
VALUES (SURNAME, FIRSTNAME, VALID)
Potrebbe essere necessario aggiungere controlli NULL
in più se i vostri campi accettano valori NULL
.
Altri suggerimenti
È possibile attivare sulla sua testa. Dipende dal rapporto di inserti di aggiornamenti anche se, come con un sacco di aggiornamenti vi sarà l'esecuzione di un sacco di inserti che non riescono.
BEGIN
INSERT INTO DSMS
(DSM, SURNAME)
VALUES
(:DSM, :SURNAME);
EXCEPTION WHEN DUP_VAL_ON_INDEX THEN
UPDATE DSMS
SET SURNAME = :SURNAME
WHERE DSM = :DSM
AND SURNAME != :SURNAME;
END;