Ad ogni modo indice univoco max 16 colonne


8

Secondo la CREATE INDEXdocumentazione:

È possibile combinare fino a 16 colonne in una singola chiave di indice composito.

Abbiamo una tabella con ~ 18 colonne che devono formare una combinazione unica. Questa tabella non lo è sensibile alle prestazioni: raramente aggiorniamo i valori / inseriamo i record. Dobbiamo solo assicurarci di evitare di duplicare i nostri record ... e abbiamo pensato di poter imporre un semplice vincolo di unicità.

Qualche idea? Sono aperto a evitare il solo indice / vincolo univoco se esiste un modo migliore.


4
Questo è un tavolo.

@Joe: non insolito in alcune circostanze quando hai combinato sottotipi simili in uno. Nel mio caso, è richiesta una chiave a 15 colonne anziché 50+ tabelle diverse. Una decisione di attuazione ...
gbn,

Mentre quello che stai chiedendo è possibile, non sono così sicuro che sia saggio. Non stai seguendo il sentiero battuto. Come tale, sei sorpreso. È più probabile che tu impari sui tuoi errori piuttosto che su quelli degli altri. A lungo termine potrebbe essere più semplice provare un approccio più convenzionale. Se pubblichi ulteriori dettagli, potremmo aiutarti con l'implementazione.
AK,

So che è passato un po 'di tempo, ma cosa ti ha impedito di utilizzare semplicemente una colonna di identità GUID?
Robert Harvey,

Risposte:


14

Aggiungi una colonna calcolata persistente che combina le 18 chiavi, quindi crea un indice univoco sulla colonna calcolata:

alter table t add all_keys as c1+c2+c3+...+c18 persisted;
create unique index i18 on t (all_keys);

Consulta la sezione Creazione di indici su colonne calcolate .

Un altro approccio è quello di creare una vista indicizzata:

create view v 
with schemabinding
as select c1+c2+c3+...+c18 as all_keys
from dbo.t;

create unique clustered index c18 on v(all_keys);

Vedi Creazione di viste indicizzate .

Entrambi gli approcci consentono un aggregato chiave parziale: aggregato c1 + c2 + c3 come k1, c4 + c5 + c6 come k2 ecc. Quindi indicizza / crea una vista indicizzata su (k1, k2, ...). Questo potrebbe essere utile per le scansioni dell'intervallo (l'indice può essere utilizzato per la ricerca su c1 + c2 + c3.

Naturalmente, tutte le +operazioni nel mio esempio sono aggregazioni di stringhe, l'operatore effettivo da utilizzare dipende dai tipi di tutte quelle colonne (ad esempio, potrebbe essere necessario utilizzare cast espliciti).

PS. Poiché i vincoli univoci vengono applicati da un indice univoco, qualsiasi restrizione sugli indici univoci si applicherà anche ai vincoli univoci:

create table t (
    c1 char(3), c2 char(3), c3 char(3), c4 char(3),
    c5 char(3), c6 char(3), c7 char(3), c8 char(3),
    c9 char(3), c10 char(3), c11 char(3), c12 char(3),
    c13 char(3), c14 char(3), c15 char(3), c16 char(3),
    c17 char(3), c18 char(3), c19 char(3), c20 char(3),
    constraint unq unique
      (c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17,c18));
go  


Msg 1904, Level 16, State 1, Line 3
The index '' on table 't' has 18 column names in index key list. 
The maximum limit for index or statistics key column list is 16.
Msg 1750, Level 16, State 0, Line 3
Could not create constraint. See previous errors.

Tuttavia, la creazione del vincolo su una colonna calcolata persistente funziona:

create table t (
    c1 char(3), c2 char(3), c3 char(3), c4 char(3),
    c5 char(3), c6 char(3), c7 char(3), c8 char(3),
    c9 char(3), c10 char(3), c11 char(3), c12 char(3),
    c13 char(3), c14 char(3), c15 char(3), c16 char(3),
    c17 char(3), c18 char(3), c19 char(3), c20 char(3),
    all_c as 
        c1+c2+c3+c4+c5+c6+c7+c8+c9+c10+c11+
        c12+c13+c14+c15+c16+c17+c18 
        persisted
        constraint unq unique (all_c));
go  

Ovviamente, la colonna persistente consuma lo spazio sul disco, quindi l'approccio potrebbe essere negativo per una tabella molto grande. L'approccio con vista indicizzata non presenta questo problema, consuma solo lo spazio per l' indice , non lo spazio per la colonna e l' indice calcolati .


1
Controlla ovviamente il limite della chiave dell'indice a 900 byte ...
gbn

1
@gbn Sì, ed è per questo che ho finito con la funzione HashBytes come suggerito da RBarryYoung. Tuttavia, ho accettato questa risposta perché forniva più di una spiegazione ed esplorazione di diversi metodi. (cioè ho imparato molto qui)
Nick B,

13

Penso che faresti molto meglio a mettere il tuo controllo dell'indice univoco su una colonna calcolata che viene generata usando HASHBYTES('MD5', ...)sulla combinazione delle tue 18 colonne.


2

Ho riscontrato questo problema e il mio DBA senior mi ha suggerito di utilizzare una funzione di controllo dell'unicità. I miei inserti sono relativamente piccoli e poco frequenti (~ 1000 righe, inseriti all'inizio di ogni mese) e la mia unica preoccupazione è far rispettare l'unicità.

CREATE FUNCTION dbo.fn_UQ_table1 ()  
RETURNS BIT

AS
BEGIN
      DECLARE @ResultBit BIT = 1

      IF EXISTS(
      SELECT COUNT(*)
      FROM [table1]
      GROUP BY [c1],[c2],[c3],[c4],[c5],[c6],
            [c7],[c8],[c9],[c10],[c11],[c12],
            [c13],[c14],[c15],[c16]
      HAVING COUNT(*) > 1)
      SELECT @ResultBit = 0

      RETURN      @ResultBit

END

SELECT dbo.fn_UQ_table1()

ALTER TABLE [table1]  
WITH NOCHECK ADD  
CONSTRAINT [CK_UQ] CHECK  (([dbo].[fn_UQ_table1]()=1))

@RBarryYoung, non ho ancora il rappresentante per commentare, ma ho avuto problemi con la soluzione HASHBYTES perché uno dei miei tipi di dati era un datetime e ho commesso l'errore newbie (?) Di non fornire l'argomento di stile facoltativo al mio Funzione CONVERT durante la conversione in varchar. Senza lo stile, si ottiene il seguente errore quando si tenta di aggiungere i PERSISTED UNIQUE NONCLUSTEREDvincoli:

"column 'key_hash' in table 'table1' cannot be persisted because 
the column is non-deterministic."

0

È possibile combinare alcuni dei valori per creare un nuovo valore univoco e archiviarlo in aggiunta ai dati correnti.

Crea una funzione definita dall'utente per creare i nuovi valori e un trigger per popolare il campo quando vengono aggiunti i dati, quindi non hai molto più sovraccarico nel mantenere il campo.

Combinare due o tre dei tuoi campi ti porterebbe al limite di 16.


-1 Non sono d'accordo con l'idea di denormalizzare la tabella per ridurre il numero di colonne.
Matt M

@Matt M - Sono interessato a sapere perché hai votato in giù la mia risposta quando non è troppo diversa dal primo suggerimento nella risposta accettata a questa domanda? Vorrei anche sapere perché non sei d'accordo, quale sarebbe la tua soluzione?
Tony,

In realtà, il tuo suggerimento è, in realtà, diverso dalla soluzione accettata. Stai sostenendo la combinazione di colonne, mentre la soluzione accettata sta sostenendo la creazione di una nuova colonna che contenga i valori combinati. La tua soluzione potrebbe potenzialmente presentare problemi di prestazioni attraverso query troppo complesse per dividere i dati utili dalle colonne combinate. Personalmente, raccomanderei la soluzione presentata da RBarryYoung che utilizza una colonna calcolata PERSISTATA HashBytes combinata inserita in un indice univoco. Al contrario, ho votato a favore della sua soluzione.
Matt M

@Matt M - Grazie per la tua spiegazione, ma ho detto "... crea un nuovo valore unico e memorizzalo in aggiunta ai dati attuali." Volevo che la nuova colonna chiave fosse un nuovo campo che integri i dati esistenti e non li sostituisca. Sono d'accordo che l'uso di un campo calcolato persistente è migliore del mio suggerimento di un UDF ma, nello spirito, la mia soluzione era la stessa.
Tony,

Sembra che ho letto male la tua soluzione e mi scuso per quello. Ciò detto, combinare alcune delle colonne non è una buona soluzione, secondo me, come la soluzione di HashBytes. Ritirerò il mio -1. Ancora una volta, mi scuso per la comprensione della lettura.
Matt M

0

Potresti andare con un trigger per insert/ update. Esegui un raggruppamento selezionato in base alle colonne con una clausola di having count(*) > 1. Se ritorna non vuoto, torna indietro.


0

Ecco cosa farei. Creerei un trigger AFTER per INSERT, UPDATE che svolge una ROW_NUMBER ()funzione e partizioni per tutte e 18 le tue colonne uniche. Se il numero massimo di righe è maggiore di uno, eseguire a ROLLBACK.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.