2016-04-28 48 views
6

Ho diverse tabelle di database e ho bisogno di assicurare che alcune colonne insieme siano sempre univoche. Attualmente utilizzo un vincolo univoco come questo:Aggiungi vincolo alla riga univoca con più di 16 colonne

ALTER TABLE [dbo].[MyTable] 
    ADD CONSTRAINT [AK_MyTable_Unique_Cols] 
    UNIQUE NONCLUSTERED ([Field_1] ASC, [Field_2] ASC, 
         [Field_3] ASC, [FkDeliveryId] ASC) 
      WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
       SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, 
       ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
GO 

Dove il tavolo si presenta così. Nota che Sum non fa parte del vincolo.

Field_1 | Field_2 | Field_3 | FkDeliveryId | Sum 
Foo  | Foo  | Bar  | 1   | 100 
Foo  | Bar  | Bar  | 1   | 900 
Bar  | Foo  | Foo  | 1   | 400 
Bar  | Foo  | Bar  | 2   | 800 // Not unique 
Foo  | Foo  | Bar  | 2   | 600 
Bar  | Foo  | Bar  | 2   | 300 // Not unique 

Ma il problema è che le tabelle vengono create dinamicamente tramite C# e alcune tabelle avranno più di 16 colonne. Così, quando ho cercato di creare il vincolo in una tabella con 52 colonne ho ottenuto questo errore:

The index '' on table 'dbo.MyTable' has 52 columns in the key list. The maximum limit for index key column list is 16. Could not create constraint or index. See previous errors.

Così ora sto cercando un'altra soluzione. La mia conoscenza di SQL è limitata all'interrogazione del DB, non ai vincoli, quindi per favore sii paziente con me. :)

La mia domanda è: come posso assicurare che nessuna riga nella mia tabella è un duplicato (in base a un numero selezionato di colonne)? Anche con più di 16 colonne?

Le tabelle possono avere un numero diverso di colonne e le colonne possono essere di tipi di dati diversi.

Ho visto this question e come l'alternativa all'hash. Funzionerà quando avrò più di 50 colonne e milioni di righe?

Il hash always be unique?

aggiornamento sulla base di commenti:

Le tabelle vengono utilizzati per memorizzare i dati da file che vengono importati. Non conosco la dimensione dei file o il numero di colonne che hanno. È fatto in un lavoro programmato, quindi il problema di prestazioni relativo alla creazione della tabella non è molto importante. I dati devono essere persistenti, tuttavia il vincolo è realmente necessario solo per assicurare che per ogni inserimento nessuna riga debba essere un duplicato. In teoria, una colonna potrebbe avere varchar(max), il che farebbe diventare la colonna hash molto grande.

+2

Questa è una domanda eccellente. Il mio primo pensiero è stato anche una colonna calcolata. Con una di queste colonne o una colonna di hash si otterrà una lunghezza di dati piuttosto lunga, questo è qualcosa che dovrete considerare se è possibile risparmiare il costo dell'elaborazione di detta colonna. Hai bisogno di creare queste tabelle al volo ogni volta, non c'è un modo per mantenerle in SQL, anche se è solo la struttura dei dati? –

+0

@RichBenner - Grazie per il tuo commento. Ho modificato la mia domanda un po '. Sì, i tavoli verranno creati al volo se non esistono. Ma una volta creati possono avere più inserimenti per diversi anni. – smoksnes

risposta

5

Nessun hash non sarà sempre univoco. Si verificano collisioni di hash.

Questo sembra un requisito strano. Di solito la chiave può essere creata su un sottoinsieme di colonne nella tabella.

Una soluzione generica che aggira il limite di 16 colonne sarebbe quella di creare una colonna calcolata che concateni tutte le colonne con un delimitatore che è improbabile che si verifichi nei dati e quindi crea un indice univoco su quello. Che è fondamentalmente uguale a quello suggerito dalla tua domanda collegata.

Tuttavia, tuttavia, è presente un limite di 900 tasti indice complessivo. Se è necessario supportare lunghezze arbitrarie di colonne tra cui varchar (max) che potrebbe superare questo limite, non è possibile farlo con vincoli dichiarativi e richiederebbe un codice procedurale. Il meglio che puoi fare è creare un hash e un indice non univoco su questo e poi fare in modo che il tuo processo di inserimento controlli eventuali duplicati di hash per vedere se sono effettivamente duplicati reali (potrebbe accadere in un trigger per assicurarsi che sia sempre selezionato o il processo ETL stesso - che sarebbe probabilmente più efficiente).

Se lo si fa in un trigger, sarebbe utile aggiungere una colonna Identity alla tabella. Quindi il codice per identificare i duplicati sarebbe.

SELECT * 
FROM Inserted I 
JOIN BaseTable B ON I.HashValue = B.HashValue AND I.Id<> B.Id 
/* check remaining columns to see if actual differences exist in null safe way 
    http://sqlblog.com/blogs/paul_white/archive/2011/06/22/undocumented-query-plans-equality-comparisons.aspx 
    */ 
    AND EXISTS (SELECT B.Col1, B.Col2 
       INTERSECT 
       SELECT I.Col1, I.Col2) 

Se quanto sopra restituisce una riga si verifica una violazione e si può eseguire il rollback della transazione.

+0

Grazie per il vostro feedback. Giusto per essere sicuro di aver capito ... Dato che avrò delle lunghezze arbitrarie delle colonne, suggerirai di avere una hash-column con un indice non univoco e di controllare invece l'unicità in un trigger? – smoksnes

+0

@smoksnes si si potrebbe fare in un trigger. Sebbene questo provenga da un file, stai caricando in SSIS? È possibile calcolare l'hash in SSIS e avere una trasformazione di ricerca su quella colonna con una suddivisione condizionale successiva che controlla i valori effettivi della colonna per vedere se sono tutti uguali. –

+0

No, non sto utilizzando SSIS. C'è un programma in esecuzione con C#. Stavo pensando di farlo prima nel livello aziendale (codice), ma ho pensato che sarebbe stato più facile e più appropriato farlo in SQL. Ma ora non ne sono più così sicuro. – smoksnes