2009-06-15 17 views
32

Le versioni di SQL Server supportano i limiti deferrabili (DC)?Vincoli rimandabili in SQL Server

A partire dalla versione 8.0, Oracle has supported deferrable constraints - vincoli che vengono valutati solo quando si esegue il commit di un gruppo di istruzioni, non quando si inseriscono o si aggiornano singole tabelle. I vincoli rimandabili differiscono dalla semplice disabilitazione/abilitazione dei vincoli, in quanto i vincoli sono ancora attivi - vengono solo valutati in un secondo momento (quando viene eseguito il commit del batch).

Il vantaggio di DC è che consentono di valutare gli aggiornamenti che individualmente sarebbero illegali e che generano cumulativamente uno stato finale valido. Un esempio è la creazione di riferimenti circolari in una tabella tra due righe in cui ogni riga richiede l'esistenza di un valore. Nessuna istruzione di inserimento individuale passerebbe il vincolo, ma il gruppo può.

Per chiarire il mio obiettivo, sto cercando di eseguire il porting di un'implementazione ORM in C# a SQLServer - sfortunatamente l'implementazione si basa su Oracle DC per evitare di inserire gli ordini di inserimento/aggiornamento/cancellazione tra le righe.

+0

Stai sostanzialmente chiedendo una variante di [questa domanda] (http://stackoverflow.com/questions/998267/deferred-constraint-checking)? –

risposta

9

Finora SQL Server non li supporta. Qual è il problema che stai risolvendo?

+0

Abbiamo un livello ORM nel nostro sistema aziendale che sfrutta DC in Oracle che potremmo voler eseguire il porting su SQL Server. Sfortunatamente, DC non sembra essere supportata, il che complica lo sforzo di portare l'implementazione. In particolare, la rete di record di modifiche dovrà essere elaborata in un ordine molto particolare (e difficile da calcolare) per evitare di violare i vincoli di RI. Sto solo cercando un modo per evitare di doverlo fare. – LBushkin

+2

Ho scritto un piccolo post su di esso qualche tempo fa. In pratica, si salvano le righe di bozza in tutte le tabelle, che si contrassegnano come completate, a quel punto si accende RI. Google up "Mimando vincoli rimandabili con colonne calcolate persistenti." –

3

Apparentemente no.

Ho trovato circa cinque diversi post del blog tutti dicendo che SQLServer (in varie versioni) non supporta i vincoli rimandabili.

D'altra parte, ho anche trovato un post che tenta di imitare questa funzione usando "persisted computed columns," (scorrere fino all'ultima voce), ma caveat emptor

+1

il link sembra essere rotto? –

3

Sembra che il problema che ho è che SQL non supporta ciò che Date e Darwen chiamano "assegnazione multipla". La risposta standard di SQL a questo era "vincoli rinviabili", che SQL Server non supporta. Un vincolo FK o CHECK di SQL Server può essere contrassegnato con NOCHECK ma non è esattamente lo stesso. Per ulteriori dettagli, vedere MSDN: ALTER TABLE (Transact-SQL).

21

OT: ci sono IMHO un bel paio di cose di SQL Server non supporta, ma avrebbe senso in un ambiente aziendale:

  • vincoli differibili, come indicato qui
  • MARS: Proprio perché hai bisogno per impostare un'opzione per qualcosa del tutto naturale?
  • Vincoli CASCADE DELETE: SQL Server consente solo un singolo percorso di sovrapposizione per un determinato vincolo CASCADE DELETE. Ancora una volta, non vedo una ragione per cui non dovrebbe essere permesso di fare cascata sulla cancellazione attraverso molteplici possibili percorsi: Alla fine, al momento in cui viene realmente eseguito, ci sarà sempre un solo percorso effettivamente utilizzato, quindi perché questa restrizione?
  • Prevenzione di transazioni parallele su una singola connessione ADO.NET.
  • Forzatura di ogni comando eseguito su una connessione che ha una transazione da eseguire all'interno di questa transazione.
  • Quando si crea un indice UNIQUE, NULL viene considerato come se fosse un valore effettivo e può apparire solo una volta nell'indice. nozione di SQL NULL come un "valore sconosciuto" sarebbe, tuttavia, indicano, che i valori NULL essere ignorato del tutto quando si crea l'indice ...

Tutte queste piccole cose fanno molti della integrità referenziale e transazionali caratteristiche che ci si aspettarsi da un RDBMS full-size quasi inutile in SQL Server.Ad esempio, poiché non sono supportati i vincoli deferrabili, la nozione di "transazione" come unità di lavoro esternamente coerente viene in parte annullata, l'unica soluzione praticabile, fatta eccezione per alcune soluzioni alternative, per non definire affatto i vincoli di integrità referenziale. Mi aspetto che il comportamento naturale di una transazione sia che puoi lavorare al suo interno nel modo e nell'ordine delle operazioni che ti piacciono, e il sistema si assicurerà che sia coerente nel momento in cui lo commetti. Problemi simili derivano dalla limitazione, che un vincolo di integrità referenziale con ON DELETE CASCADE può essere definito solo in modo che solo un singolo vincolo possa portare alla cancellazione a cascata di un oggetto. Questo in realtà non si adatta alla maggior parte degli scenari del mondo reale.

+2

Ora è possibile avere indici univoci contenenti valori null utilizzando un indice filtrato. – LMK

1

Se si dispone di un proprio livello ORM, una soluzione al problema potrebbe essere quella di separare l'aggiornamento dell'oggetto dall'aggiornamento di riferimento dalla logica del livello ORM. tuo ORM avrebbe poi lavorare con le transazioni in base al cambiamento lato client insieme in diverse fasi:

  1. eliminare tutti i riferimenti di chiave esterna definiti da impostare come il cambiamento di essere eliminati, ovvero un set corrispondente colonne chiave esterna NULL, o , per le relazioni che utilizzano le tabelle di mappatura, CANCELLARE le voci dalle tabelle di mappatura come appropriato.
  2. Eliminare tutti gli oggetti definiti come "cancellato" dal cambiamento imposta
  3. Creare tutti i nuovi oggetti nel set di cambiamento, ma non hanno ancora fissato le colonne chiave esterna
  4. aggiornare tutte le variazioni di valore "primitive" su qualsiasi oggetti aggiornati in il set di modifiche, ovvero non aggiorna le colonne chiave esterna
  5. Imposta i valori delle colonne chiave esterna come definito nel set di modifiche.
  6. mappature tabella di mappatura aggiuntivi per i rapporti basati su tabelle di mappatura
  7. commettere

Questo dovrebbe risolvere il problema, dal momento che esistono tutti gli oggetti di cui si fa riferimento in qualsiasi momento un valore chiave esterna è impostato ...

+0

Oppure utilizzare una stored procedure e collegarla a Insert() Update() di quell'entità nell'ORM (Entity Framework e Linq to SQL consente questo http://stackoverflow.com/questions/5346601/stored-procedures-and- ORM) –

1

Esiste un metodo per aggirare l'imposizione dei vincoli posticipati mancanti in determinate condizioni (a gennaio 2017 non è disponibile alcun supporto per i vincoli posticipati in SQL Server). Si consideri il seguente schema di database:

Disclaimer: La qualità dello schema, o il caso d'uso, non è per un dibattito qui, si è dato come un esempio di base per la soluzione

CREATE TABLE T (Id TYPE NOT NULL PRIMARY KEY, NextId TYPE NOT NULL); 

ALTER TABLE T WITH CHECK ADD CONSTRAINT FK_T2T 
FOREIGN KEY (NextId) REFERENCES T (Id); 

CREATE UNIQUE NONCLUSTERED INDEX UC_T ON T (NextId); 

Dove TYPE è un tipo di dati adatto per una chiave surrogata. L'ipotesi è che il valore per la chiave surrogata sia assegnato da RDBMS durante l'operazione INSERT (ad esempio IDENTITY).

Il caso d'uso è di mantenere la versione "più recente" dell'entità T con NextId = NULL e memorizzare le versioni precedenti mantenendo un elenco a collegamento singolo T.NextId -> T.Id.

Ovviamente, lo schema dato è soggetto al problema del vincolo posticipato poiché l'inserto della nuova versione "più recente" deve precedere l'aggiornamento del vecchio "ultimo" e durante tale periodo ci saranno due record nel database con lo stesso valore NextId.

Ora, se:

Il tipo di dati della chiave primaria non deve essere numerico, e può essere calcolato in anticipo (cioèUNIQUEIDENTIFIER), allora il problema vincolo differito è aggirato utilizzando MERGE, in questo modo:

DECLARE TABLE @MergeTable TABLE (Id UNIQUEIDENTIFIER); 

DECLARE @NewLatestVersion UNIQUEIDENTIFIER = NEWID(); 

INSERT INTO @MergeTable (Id) VALUES (@NewLatestVersion); 
INSERT INTO @MergeTable (Id) VALUES (@OldLatestVersion); 

MERGE INTO T 
USING @MergeTable m ON T.Id = m.Id 
WHEN MATCHED THEN UPDATE SET T.NextId = @NewLatestVersion 
WHEN NOT MATCHED THEN INSERT (Id) VALUES (@NewLatestVersion); 

A quanto pare, unire dichiarazione completa tutte le manipolazioni di dati prima di controllare i vincoli.