2009-06-05 11 views
7

Che tipo di trucchi SQL si utilizzano per inserire i dati in due tabelle con un riferimento circolare in mezzo.Gestione del riferimento circolare quando si immettono dati in SQL

Employees 
    EmployeeID <PK> 
    DepartmentID <FK> NOT NULL 

Departments 
    DepartmentID <PK> 
    EmployeeID <FK> NOT NULL 

Il dipendente appartiene ad un reparto, un reparto deve avere un gestore (funzionari).

Devo disabilitare i vincoli per l'inserimento?

+0

Questo mi sembra uno strano schema per me, si può spiegare il motivo per cui si hanno le relazioni in questo modo? – Bob

+3

È EmployeeId nella tua tabella di dipartimento come un responsabile di reparto o qualcosa del genere? –

+0

In Oracle, il "trucco" è definire le chiavi esterne DEFERRABLE. – spencer7593

risposta

5

Q: Devo disabilitare i vincoli per l'inserimento?
A: In Oracle, no, non se i vincoli di chiave esterna sono DEFERRABLE (vedi esempio qui sotto)

Per Oracle:

 
    SET CONSTRAINTS ALL DEFERRED; 
    INSERT INTO Departments values ('foo','dummy'); 
    INSERT INTO Employees values ('bar','foo'); 
    UPDATE Departments SET EmployeeID = 'bar' WHERE DepartmentID = 'foo'; 
    COMMIT; 

Diamo disfare che:

  • (l'autocommit deve essere disattivato)
  • posticipare l'applicazione del vincolo di chiave esterna
  • inserire una riga alla tabella Department con un valore "fittizio" per la colonna FK
  • inserire una riga di tabella Impiegato con FK riferimento alle attrezzature
  • sostituire valore "fittizio" in attrezzature FK con riferimento reale
  • ri-abilitare l'applicazione dei vincoli

NOTE: disattivazione di un vincolo di chiave esterna ha effetto per tutte le sessioni, rinviando un vincolo è a un livello di transazione (come nell'esempio), o al livello di sessione (ALTER SESSION SET CONSTRAINTS=DEFERRED;)

Oracle ha consentito che i vincoli di chiave esterna siano definiti DEFERIBILI per almeno un decennio. Definisco tutti i vincoli di chiave estera (naturalmente) da DEFERRABLE INIZIALMENTE IMMEDIATO. Ciò mantiene il comportamento predefinito come tutti si aspettano, ma consente la manipolazione senza che le chiavi esterne siano disabilitate.

vedere AskTom: http://www.oracle.com/technology/oramag/oracle/03-nov/o63asktom.html

vedere AskTom: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:10954765239682

vedi anche: http://www.idevelopment.info/data/Oracle/DBA_tips/Database_Administration/DBA_12.shtml

[EDIT]

A: In Microsoft SQL Server, non è possibile rimanda i vincoli delle chiavi esterne come puoi in Oracle. Disabilitare e riattivare il vincolo di chiave esterna è un approccio, ma rabbrividisco alla prospettiva di 1) impatto sulle prestazioni (il vincolo di chiave esterna viene controllato per la tabella INTERA quando il vincolo è riattivato), 2) gestendo l'eccezione se (quando?) la riattivazione del vincolo fallisce. Si noti che la disattivazione del vincolo influirà su tutte le sessioni, quindi, mentre il vincolo è disabilitato, altre sessioni potrebbero potenzialmente inserire e aggiornare le righe, il che causerà il fallimento della restrizione del vincolo.

Con SQL Server, un approccio migliore consiste nel rimuovere il vincolo NOT NULL e consentire un NULL come segnaposto temporaneo mentre le righe vengono inserite/aggiornate.

Per SQL Server:

 
    -- (with NOT NULL constraint removed from Departments.EmployeeID) 
    insert into Departments values ('foo',NULL) 
    go 
    insert into Employees values ('bar','foo') 
    go 
    update Departments set EmployeeID = 'bar' where DepartmentID = 'foo' 
    go 

[/ EDIT]

+1

Hai appena scoperto che Sybase ti consente di farlo anche con SET OPTION WAIT_FOR_COMMIT = 'ON'. Impostandolo su ON si ritarderà il controllo referenziale finché non si chiama COMMIT. SET OPTION WAIT_FOR_COMMIT = 'ON'; inserire in Dipendenti ... inserire in reparti ... COMMIT; SET OPTION WAIT_FOR_COMMIT = 'OFF'; Ora manca solo MSSQL;) – Tom

+0

-1 "un approccio migliore è rimuovere il vincolo NOT NULL" - come si impedisce al "segnaposto temporaneo" di rimanere indefinitamente. – onedaywhen

+0

@ Tom: grazie per la nota utile, sull'abilitazione del "controllo dei vincoli posticipati" su Sybase. – spencer7593

0

Sì, in questo caso sarà necessario disabilitare una chiave esterna.

1

Riforma lo schema rimuovendo il riferimento circolare.
Elimina una colonna ID da uno qualsiasi degli schemi della tabella.

Dipartimenti. L'ID dell'impiegato non sembra appartenere a questo a mio parere.

1

Non riesco a pensare a un modo non hackish di farlo. Penso che dovrai rimuovere il vincolo o fare qualche tipo di valori fittizi che vengono aggiornati dopo tutti gli inserti.

Suggerirei di refactoring lo schema del DB. Non riesco a pensare ad alcun motivo per cui vorresti che funzionasse in questo modo.

Forse qualcosa come Employee, EmployeeDepartment (EmployeeId, DepartmentId) e Department sarebbe un modo migliore per raggiungere lo stesso obiettivo.

0

È necessario eliminare definitivamente l'uno o l'altro riferimento. Questa non è una struttura progettuale praticabile. Quale deve essere inserito per primo? Dipartimento o Dipendente? A meno che i reparti non siano tutti un dipendente di grandi dimensioni, la struttura non ha comunque senso poiché ogni dipendente dovrebbe avere un dipartimento distinto.

+0

Non so perché non è una struttura di progettazione valida. Il dipendente appartiene a un dipartimento, un dipartimento deve avere un manager (capo dipartimento). – Tom

+1

Nessun dipartimento non deve avere un manager. Le posizioni di capo reparto diventano vacanti. Ma non è fattibile perché non si può mai avere una situazione circolare pk/fk e farlo funzionare con l'hacking per aggirare i vincoli. Il bisogno di hackerare per aggirare i vincoli è il primo indizio che il design sia cattivo. Diverò una tabella dei dipendenti che è la tabella genitore, una tabella organizzativa che include la struttura organizzativa e quindi una tabella delle assegnazioni che ha la struttura a cui è assegnata la persona e la gerarchia (che si riferisce a). Quale ha l'FK per entrambi. – HLGEM

13

Presumo che i vostri dipartimenti.ImployeeID è un capo dipartimento. Quello che farei è rendere nullable quella colonna; quindi puoi creare prima il dipartimento, poi il dipendente.

+1

Nessun motivo per cui non è possibile avere una chiave esterna su un campo Null. – Bill

+0

No, non puoi. Ma puoi comunque aggiungere un indice per aiutarti a partecipare ... –

+0

Caos: Certamente questa è un'opzione. Bill: L'idea era che ogni dipartimento dovesse avere un capo reparto. – Tom

1

Si potrebbe creare una riga nella tabella dipartimento per 'Non assegnati'

Per creare un nuovo reparto con un nuovo dipendente allora sarebbe

  1. Crea il Dipendente (EmployeeA) nella 'Non assegnati' Dipartimento
  2. Creare il nuovo reparto (departmentA) con il dipendente EmployeeA
  3. Aggiornamento EmployeeA di essere in departmentA

Ciò non invalida lo schema corrente e è possibile impostare un'attività da eseguire regolarmente per verificare che non vi siano membri del reparto Non assegnato.

Si sarebbe anche necessario creare un impiegato di default di essere l'impiegato non assegnati

EDIT:

La soluzione proposta dal caos è molto più semplice se

3

Questo problema potrebbe essere risolto con i vincoli deferable .Tali vincoli vengono controllati quando viene eseguita l'intera transazione, consentendo così di inserire sia il dipendente che il reparto nella stessa transazione, facendo riferimento l'uno all'altro. (Supponendo che il modello di dati abbia senso)

+0

Sembra l'approccio descritto da Spencer7593. Qualcuno sa come farlo in MSSQL? – Tom

1

Ci sono alcuni buoni disegni che ho usato. Tutti implicano la rimozione del EmployeeID "manager" dalla tabella Department e la rimozione del DepartmentID dalla tabella Employee. Ho visto un paio di risposte che lo menzionano, ma chiarirò il modo in cui lo abbiamo usato:

In genere termino con una tabella di collegamento delle relazioni EmployeeDepartment - molte a molte, in genere con flag come IsManager, IsPrimaryManager, IsAdmin, IsBackupManager ecc., Che chiarisce la relazione Alcuni possono essere vincolati in modo tale che vi sia un solo Manager Primario autorizzato per dipartimento (sebbene una persona possa essere un PrimaryManager di più reparti). Se non ti piace la tabella singola, puoi avere più tabelle: EmployeeDepartment, ManagerDepartment, ecc., Ma potresti avere situazioni in cui una persona è un manager ma non un dipendente, ecc.

persone per essere membri di più reparti.

Per l'accesso semplificato, è possibile fornire viste che eseguono l'unione in modo appropriato.

Problemi correlati