2012-02-27 10 views
8

Utilizzo Microsoft SQL Server 2008 R2 (con il service pack/patch più recente) e le regole di confronto del database sono SQL_Latin1_General_CP1_CI_AS.Comportamento dell'indice univoco, colonna varchar e spazi (vuoti)

Il codice seguente:

SET ANSI_PADDING ON; 
GO 

CREATE TABLE Test (
    Code VARCHAR(16) NULL 
); 
CREATE UNIQUE INDEX UniqueIndex 
    ON Test(Code); 

INSERT INTO Test VALUES ('sample'); 
INSERT INTO Test VALUES ('sample '); 

SELECT '>' + Code + '<' FROM Test WHERE Code = 'sample  '; 
GO 

produce i seguenti risultati:

(1 row (s) affected)

Messaggio 2601, livello 14, stato 1, riga 8

Impossibile inserire la riga chiave duplicata nell'oggetto 'dbo.Test' con indice univoco 'UniqueIndex'. Il valore della chiave duplicata è (campione).

La dichiarazione è stata chiusa.

‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐

> campione <

(1 row (s) affected)

La mia domanda s sono:

  1. Presumo che l'indice non può memorizzare spazi finali. Qualcuno può indicarmi la documentazione ufficiale che specifica/definisce questo comportamento?
  2. Esiste un'impostazione per modificare questo comportamento, cioè, riconoscere "campione" e "campione" come due valori diversi (che sono, tra l'altro) in modo che entrambi possano essere nell'indice.
  3. Perché su terra è il SELECT restituendo una riga? SQL Server deve fare qualcosa di veramente divertente/intelligente con gli spazi nella clausola WHERE perché se rimuovo l'unicità nell'indice, entrambi gli INSERT funzioneranno correttamente e SELECT restituirà due righe!

Qualsiasi aiuto/puntatore nella giusta direzione sarebbe apprezzato. Grazie.

risposta

11

Trailing blanks explained:

SQL Server segue la specifica ANSI/ISO SQL-92 (Sezione 8.2, , le regole generali # 3) sul modo di confrontare le stringhe con spazi. Lo standard ANSI richiede il riempimento per le stringhe di caratteri utilizzate nei confronti in modo che la loro lunghezza corrisponda a confrontandole. Il riempimento influisce direttamente sulla semantica di WHERE e sui predicati della clausola HAVING e su altri confronti della stringa Transact-SQL . Ad esempio, Transact-SQL considera le stringhe "abc" e "abc" equivalenti per la maggior parte delle operazioni di confronto.

L'unica eccezione a questa regola è il predicato LIKE.Quando il lato destro di un'espressione di predicato LIKE presenta un valore con uno spazio iniziale , SQL Server non applica i due valori alla stessa lunghezza prima del confronto. Poiché lo scopo del predicato LIKE , per definizione, è di facilitare le ricerche di pattern piuttosto che rispetto ai semplici test di uguaglianza delle stringhe, ciò non viola la sezione della specifica ANSI SQL-92 menzionata in precedenza.

Ecco un esempio ben noto di tutti i casi sopra citati:

DECLARE @a VARCHAR(10) 
DECLARE @b varchar(10) 

SET @a = '1' 
SET @b = '1 ' --with trailing blank 

SELECT 1 
WHERE 
    @a = @b 
AND @a NOT LIKE @b 
AND @b LIKE @a 

Ecco qualche dettaglio circa trailing blanks and the LIKE clause.

indici quanto riguarda:

Un inserimento in una colonna i cui valori devono essere univoci fallirà se si fornisce un valore che si differenzia dai valori esistenti solo spazi finali. Le seguenti stringhe verranno considerate equivalenti a da un vincolo univoco, una chiave primaria o un indice univoco. Allo stesso modo, se si dispone di una tabella esistente con i dati di seguito e si tenta di aggiungere una limitazione univoca, , avrà esito negativo poiché i valori sono identificati come .

PaddedColumn 
------------ 
'abc' 
'abc ' 
'abc ' 
'abc ' 

(Tratto da here.)

+2

Grazie per i puntatori, ragazzi. Mea culpa per essere troppo pigro con Google da solo. Secondo me, il comportamento definito dallo standard non è intuitivo. Immagino che 9 sviluppatori su 10 direbbero che 'a' e 'a' NON sono la stessa stringa, ma vabbè. – Eric