2014-12-10 8 views
6

Supponiamo che io ho la seguente tabella con i seguenti vincoli:Convalida indice/vincolo univoco istruzione MERGE per riga o per istruzione?

create table test as (
    select 1 as id, 'a' as name from dual 
    union all 
    select 2, 'b' from dual 
    union all 
    select 3, 'c' from dual 
); 

create unique index ind on test(name); 

alter table test add constraint constr unique (name); 

select * from test; 

     ID NAME 
---------- ---- 
     1 a 
     2 b 
     3 c 

Supponiamo ora che faccio la seguente MERGE:

merge into test t using (
    select 4 as id, 'b' as name from dual 
    union all 
    select 2 as id, null as name from dual 
) s on (s.id = t.id) 
    when matched then update set t.name = s.name 
    when not matched then insert(t.id, t.name) values(s.id, s.name) 

select * from test; 

     ID NAME 
---------- ---- 
     1 a 
     2  
     3 c 
     4 b 

disposta la sopra MERGEmai fallire? Se è UPDATE s prima, e quindi INSERT s, l'indice/vincolo non verrà invalidato durante l'esecuzione. Ma se prima è INSERT s, e quindi UPDATE s, l'indice sarà temporaneamente invalidato e l'istruzione potrebbe fallire ?.

Qualcuno può spiegare in dettaglio (o indicare nella giusta direzione) in che modo Oracle RDBMS gestisce tali problemi? Inoltre, la gestione è la stessa quando si utilizza la clausola LOG ERRORS INTO?

ragione principale per cui chiedo a questa domanda e il motivo per cui ho bisogno di una soluzione: ho FUSIONE dichiarazioni in esecuzione per diverse ore con errori accedere al clausola. La registrazione degli errori sembra funzionare come una transazione autonoma. Alcuni errori di vincoli univoci (basati su indici univoci) vengono registrati molto prima che l'istruzione finisca in aumento (tra gli altri, vedo che la sequenza sta salendo), e non so perché (anche se alla fine, dopo l'upsert, non dovrebbe esserci un vincolo univoco invalidato). Quando guardo la tabella ERROR, vedo ORA-00001: vincolo univoco (XXX.YYY) violato su un'operazione INSERT. Posso inserire questo record dalla tabella ERROR nella tabella principale senza causare un errore di vincolo univoco. Quindi mi chiedo perché l'errore viene registrato in primo luogo.

MODIFICA: Le risposte seguenti indicano che quando viene eseguita un'istruzione, i vincoli vengono applicati alla fine dell'istruzione. Comprendo e accetto (mentre vorrei conoscere maggiori dettagli sulla manutenzione degli indici in tali scenari). Quello che non capisco e il motivo per cui questa domanda non è ancora stata risolta è il motivo per cui sto avendo questi ORA-00001: gli errori violati del vincolo univoco (XXX.YYY) registrati mentre non dovrebbero essere. Sembra che il meccanismo di registrazione degli errori non si comporti in modo atomico.

EDIT2:

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production 
PL/SQL Release 11.2.0.4.0 - Production 
CORE 11.2.0.4.0 Production 
TNS for Solaris: Version 11.2.0.4.0 - Production 
NLSRTL Version 11.2.0.4.0 - Production 

Edit3: ho giocato un po 'ed è stato in grado di riprodurre questo errore:

drop table test; 

drop table err_test; 

create table test as (
    select 1 as id, 'a' as name from dual 
    union all 
    select 2, 'b' from dual 
    union all 
    select 3, 'c' from dual 
); 

create unique index ind on test(name); 

alter table test add constraint constr unique (name); 

--select test.rowid, test.* from test; 

BEGIN 
DBMS_ERRLOG.CREATE_ERROR_LOG (
    dml_table_name   => 'TEST', 
    err_log_table_name  => 'ERR_TEST'); 
END; 
/

--truncate table err_test; 

select * from err_test; 

merge /*+ PARALLEL(t 2) */ into test t using (
    select 4 as id, 'b' as name from dual 
    union all 
    select 2 as id, null as name from dual 
) s on (s.id = t.id) 
    when matched then update set t.name = s.name 
    when not matched then insert(t.id, t.name) values(s.id, s.name) 
LOG ERRORS INTO ERR_TEST('TEST,ID:'||s.id) REJECT LIMIT UNLIMITED; 

select * from err_test; 

Nell'ultimo select * from err_test; ottengo sempre: ORA-00001: unique constraint (XXX.CONSTR) violated. Ora la cosa strana è che la dichiarazione vera e propria fusione (in produzione) non lavorare in parallelo più, e ho ancora ottenere questo errore a volte ...

edit4: La migliore risposta che ho contrassegnato come accettato , anche se la domanda stessa non ha una risposta completa. Sembra che sia solo un bug in Oracle.

+1

Quale versione di database esatto * è in esecuzione? Metalink contiene un paio di documenti sui bug relativi a MERGE e ORA-00001. –

+1

Alos, hai provato a eseguire MERGE senza registrazione degli errori? Metalink 17449815 ("L'incoerenza dell'indice è stata osservata per un MERGE sql con un ramo UPDATE sia INSERT che e anche ERROR LOGGING (clausola LOG ERRORS INTO)") potrebbe essere rilevante. –

+0

@FrankSchmitt Buona idea vederlo come un bug. La versione è 11.2.0.4.0. Lo esaminerò. Ho provato a eseguirlo senza la clausola ERROR LOGGING, ma non ho mai riscontrato alcun vincolo. È un ambiente di produzione, quindi non posso rimuovere la clausola indefinitamente. –

risposta

5

Questa unione non ha mai esito negativo.

Ciò si spiega con esempi qui: Database Concepts - 5. Data Integrity

Per un vincoli non defferrable (default):

In a nondeferrable constraint, Oracle Database never defers the validity check of the constraint to the end of the transaction. Instead, the database checks the constraint at the end of each statement. If the constraint is violated, then the statement rolls back.



I mezzi di cui sopra, che i vincoli sono controllati alla fine dell'intero singola Istruzione SQL, ma non durante la loro esecuzione.



Sotto, in questa documentazione, si possono trovare due esempi di transazioni, che "internamente", durante la loro esecuzione, violare alcune regole di vincolo, ma alla fine sono tutte soddisfatte vincolo, e non ci sono legali, a causa :

... because the database effectively checks constraints after the statement completes. Figure 5-4 shows that the database performs the actions of the entire SQL statement before checking constraints.

alla fine hanno anche scritto che:

The examples in this section illustrate the constraint checking mechanism during INSERT and UPDATE statements, but the database uses the same mechanism for all types of DML statements. The same mechanism is used for all types of constraints, not just self-referential constraints.

+0

Grazie per il comando. Nota che i vincoli sono solo una parte della domanda. L'elaborazione dell'indice univoca è qualcosa di completamente diverso. Tornerò dopo aver esaminato la documentazione. –

+0

Per quanto riguarda i vincoli, hai assolutamente ragione: sono controllati alla fine dell'istruzione e non dovrebbero causare problemi. Ma trovo difficile capire come lo stesso valga per gli indici. Gli indici vengono mantenuti durante l'esecuzione e per gli indici univoci il rowid non viene considerato "parte della chiave". Ho appena esaminato il _Concepts Manual_ e _Kyte's DB Architecture_, ma non ho trovato nulla di utile sulla gestione dell'indice all'interno di un'istruzione. –

+0

Ma perché ti preoccupi della manutenzione degli indici durante un aggiornamento? Dicono chiaramente: la tua dichiarazione SQL non fallisce mai a meno che ** i vincoli non vengano violati alla fine dell'esecuzione della transazione. **. Una transazione di database deve essere ACID: http://en.wikipedia.org/wiki/ACID, soprattutto deve essere conforme alle regole di coerenza: se per esempio esiste un vincolo 'A + B = 100', la regola deve essere indipendente da l'ordine delle operazioni interne all'interno della transazione. Se l'ordine è 'A + B = 100' o 'B + A = 100', deve avere successo in entrambi i casi, non può fallire nel secondo caso 'B + A = 100' perché l'ordine è sbagliato – krokodilko

4

"Log ERRORI in" parte del lavoro, come gli altri utenti hanno indicato, avviene dopo che la dichiarazione è stata eseguita (Aggiornamento di una inserire parte), mentre vincoli di controllo. Quindi è possibile inserire errori prima che il controllo dei vincoli sia terminato. Ecco perché viene visualizzato l'errore inserito prima che l'istruzione sia completa.

E come una risposta per questa osservazione:

I can insert this record from the ERROR table into main table without causing unique constraint failure. So I wonder why the error is logged in the first place.

essere sicuri di avere l'intera informazione in una dichiarazione di unione. se non si aggiorna il valore nella stessa dichiarazione ma in un'altra che si verifica tra l'inserimento non riuscito e il tentativo, le cose sono spiegabili.

(Quello che voglio dire è il record nella parte utilizzando non sono nella stessa istruzione

  • Sessione 1:. Merge utilizzando select 4 as id, 'b' as name from dual (l'errore è inserito nel registro)
  • sessione 2: merge utilizzando select 2 as id, null as name from dual commettere ok
  • sessione 3: si ritentare l'inserto e funziona

)

Se è possibile riprodurre l'errore con una dichiarazione, sarebbe un problema. Ma hai molte sessioni nel tuo ambiente. Si prega di controllare la fonte delle vostre dichiarazioni di fusione. Potresti avere un arrivo in ritardo o qualcosa del genere.

+0

Afaik, nessun'altra affermazione sta manipolando il tavolo. Dal messaggio $ ora_err_tag della tabella ERR posso concludere che l'istruzione MERGE sopra riportata sta causando l'errore. Hai alcuni punti validi però. Li esaminerò. Ho anche aggiornato la mia domanda (edit3). –