2009-09-09 6 views
72

Questa mattina mi sono imbattuto in qualcosa di strano e ho pensato di inviarlo per un commento.SQL Server 2008 Empty String vs. Space

qualcuno può spiegare il motivo per cui le seguenti query SQL stampe 'uguale' quando esegue SQL 2008. è impostare il livello di compatibilità dB a 100.

if '' = ' ' 
    print 'equal' 
else 
    print 'not equal' 

E questo restituisce 0:

select (LEN(' ')) 

Sembra che si stia ritagliando automaticamente lo spazio. Non ho idea se questo fosse il caso nelle versioni precedenti di SQL Server, e non ho più alcuna possibilità di testarlo.

Mi sono imbattuto in questo perché una query di produzione restituiva risultati errati. Non riesco a trovare questo comportamento documentato ovunque.

Qualcuno ha qualche informazione in merito?

+2

SQL 2005: selezionare len ('') restituisce 0 – Mayo

+1

Fa lo stesso su Sql Server 2000. –

+1

Questa è una domanda affascinante. Sembra che ritorni uguale, non importa quanti spazi hai messo in nessuna delle due stringhe, sia che corrispondano o meno. Dopo ulteriori sperimentazioni ho notato che sta effettivamente facendo un RTRIM su entrambi i lati dell'operatore di uguaglianza prima del confronto. Sembra che hai una risposta sulla funzione LEN, ma sono veramente interessati a una risposta più approfondita rispetto "VARCHAR e l'uguaglianza sono spinose in TSQ" per la parte uguaglianza della tua domanda. – JohnFx

risposta

74

varchar s e l'uguaglianza sono spinosi in TSQL. La funzione LEN dice:

Restituisce il numero di caratteri, piuttosto che il numero di byte, della data espressione di stringa, escludendo gli spazi finali.

è necessario utilizzare DATALENGTH per ottenere un vero e proprio byte conteggio dei dati in questione. Se si dispone di dati Unicode, si noti che il valore ottenuto in questa situazione non sarà uguale alla lunghezza del testo.

print(DATALENGTH(' ')) --1 
print(LEN(' '))  --0 

Quando si tratta di uguaglianza di espressioni, le due stringhe vengono confrontate per l'uguaglianza in questo modo:

  • si accorciano stringa
  • Pad con spazi vuoti fino lunghezza pari a quella della stringa più lunga
  • Confronta i due

È il centro il passo che sta causando risultati inaspettati - dopo quel passo, stai effettivamente confrontando gli spazi bianchi con gli spazi bianchi - quindi sono visti come uguali.

LIKE si comporta meglio di = nella situazione "spazi vuoti", perché non esegue vuoto-padding sul modello che stavi cercando di abbinare:

if '' = ' ' 
print 'eq' 
else 
print 'ne' 

Will dare eq mentre:

if '' LIKE ' ' 
print 'eq' 
else 
print 'ne' 

Dare ne

Attenzione: con LIKE tuttavia: non è simmetrico: tratta gli spazi bianchi finali come significativi nel modello (RHS) ma non nell'espressione di corrispondenza (LHS). Quanto segue è tratto da here:

declare @Space nvarchar(10) 
declare @Space2 nvarchar(10) 

set @Space = '' 
set @Space2 = ' ' 

if @Space like @Space2 
print '@Space Like @Space2' 
else 
print '@Space Not Like @Space2' 

if @Space2 like @Space 
print '@Space2 Like @Space' 
else 
print '@Space2 Not Like @Space' 

@Space Not Like @Space2 
@Space2 Like @Space 
+1

Bella risposta. Non l'avevo notato nella documentazione LEN. Tuttavia non è limitato alla LEN. La funzione DESTRA e SINISTRA presenta un comportamento simile, ma non è documentata. Sembra essere il letterale con uno spazio che causa il problema. Ho notato che questo torna anche uguale: se '' = SPACE (1) \t print 'uguale' altro \t stampa 'è uguale a' non sto veramente interessati a ottenere la vera lunghezza, ero solo confuso perché quando cercavo uno spazio in una colonna, venivano restituite tutte le colonne che erano stringhe vuote. – jhale

+0

Inoltre, belle informazioni sull'istruzione LIKE.Immagino che la morale della storia sia cercare di non metterti nella posizione in cui devi confrontare uno spazio e una stringa vuota. – jhale

+2

Il problema è maggiore rispetto al confronto di uno spazio con una stringa vuota. Il confronto di due stringhe che terminano in un numero diverso di spazi mostra lo stesso comportamento. – JohnFx

4

C'era una domanda simile qualche tempo fa in cui ho guardato in un problema simile here

Invece di LEN (' '), utilizzare DATALENGTH ('') - che si dà il valore corretto.

Le soluzioni dovevano utilizzare una clausola LIKE come spiegato nella mia risposta in là e/o includere una seconda condizione nella clausola WHERE per controllare anche DATALENGTH.

Leggere la domanda e i collegamenti.

9

ho trovato questo blog article che descrive il comportamento e spiega perché.

Lo standard SQL richiede che stringa confronti, in modo efficace, il pad più breve stringa con caratteri di spazio. Questo porta al sorprendente risultato che N '' = N'' (stringa vuota uguaglia una serie di uno o più spazio caratteri) e più in generale qualsiasi stringa uguale un'altra stringa se differiscono solo per spazi finali . Questo può essere un problema in alcuni contesti.

Maggiori informazioni sono disponibili anche in MSKB316626

+0

Grazie. Sono sorpreso che sia nello standard. Sono sicuro che qualcuno molto più intelligente di me abbia avuto una buona ragione per questo. – jhale

+0

@John: intendevi scrivere ≠ (non è uguale) nel tuo commento? –

+0

La citazione originale aveva un errore in esso che ho copiato direttamente. Ho aggiornato la citazione per riflettere su ciò che intendeva l'autore originale. – JohnFx

14

L'operatore = è una T-SQL non è tanto "uguale" come è "sono la stessa parola/frase, secondo le regole di confronto dell'espressione del contesto, "e LEN è" il numero di caratteri nella parola/frase. " Le regole di confronto non trattano gli spazi finali come parte della parola/frase che li precede (sebbene trattino gli spazi vuoti iniziali come parte della stringa che precedono).

Se è necessario distinguere "questo" da "questo", non utilizzare l'operatore "sono la stessa parola o frase" perché "questo" e "questo" sono la stessa parola.

Contribuire alla strada = funziona è l'idea che l'operatore di uguaglianza delle stringhe debba dipendere dai contenuti degli argomenti e dal contesto di confronto dell'espressione, ma non dovrebbe dipendere dai tipi degli argomenti, se essi sono entrambi i tipi di stringa.

Il concetto di linguaggio naturale di "questi sono la stessa parola" non è in genere abbastanza preciso da poter essere catturato da un operatore matematico come =, e non c'è alcun concetto di tipo di stringa in linguaggio naturale. Il contesto (cioè la collazione) è importante (ed esiste nel linguaggio naturale) e fa parte della storia, e ulteriori proprietà (alcune che sembrano stravaganti) fanno parte della definizione di = per renderlo ben definito nel mondo innaturale di dati.

Sul problema del tipo, non si desidera che le parole cambino quando vengono memorizzate in tipi di stringa diversi. Ad esempio, i tipi VARCHAR (10), CHAR (10) e CHAR (3) possono contenere tutte le rappresentazioni della parola "gatto" e? = 'gatto' dovrebbe permetterci di decidere se un valore di uno di questi tipi contiene la parola 'gatto' (con problemi di caso e accento determinati dalla collazione).

Risposta al commento di JohnFx:

Vedi Using char and varchar Data nella documentazione in linea. Citando da quella pagina, sottolineatura mia:

Ogni valore di dati char e varchar ha un confronto. Le regole di confronto definiscono gli attributi come gli schemi di bit utilizzati per rappresentare ciascun carattere, regole di confronto e la sensibilità al caso o all'accento.

Sono d'accordo che potrebbe essere più facile da trovare, ma è documentato.

Vale la pena notare anche che la semantica di SQL, dove = ha a che fare con i dati del mondo reale e il contesto del confronto (al contrario di qualcosa sui bit memorizzati sul computer) è stata parte di SQL per un a lungo. La premessa di RDBMS e SQL è la rappresentazione fedele dei dati del mondo reale, quindi il suo supporto per le collazioni molti anni prima che idee simili (come CultureInfo) entrassero nel regno delle lingue simili ad Algol. La premessa di quelle lingue (almeno fino a poco tempo fa) era la risoluzione dei problemi in ingegneria, non la gestione dei dati aziendali. (Recentemente, l'uso di linguaggi simili in applicazioni non ingegneristiche come la ricerca sta facendo qualche incursione, ma Java, C# e così via stanno ancora lottando con le loro radici non commerciali.)

A mio parere, non è giusto criticare SQL per essere diverso da "maggior parte dei linguaggi di programmazione". SQL è stato progettato per supportare un framework per la modellazione dei dati business che è molto diverso dall'ingegnerizzazione, quindi la lingua è diversa (e migliore per il suo obiettivo).

Heck, quando SQL è stato specificato per la prima volta, alcune lingue non avevano alcun tipo di stringa incorporato. E in alcune lingue ancora, l'operatore di uguaglianza tra stringhe non confronta affatto i dati di carattere, ma confronta i riferimenti! Non mi sorprenderebbe se tra un decennio o due, l'idea che == fosse dipendente dalla cultura diventasse la norma.

+0

BOL descrive così l'operatore =: "Confronta l'uguaglianza di due espressioni (un operatore di confronto)." Se il comportamento è corretto o meno, devi ammettere che è estremamente confuso e non standard in termini di utilizzo di questo operatore nella maggior parte dei linguaggi di programmazione. MS dovrebbe almeno aggiungere un avvertimento alla documentazione su questo comportamento. – JohnFx

+0

@JohnFx: vedere la mia risposta troppo lunga per un commento nella mia risposta. –

3

Per confrontare un valore ad uno spazio letterale, si può anche usare questa tecnica come alternativa alla dichiarazione LIKE:

IF ASCII('') = 32 PRINT 'equal' ELSE PRINT 'not equal' 
0

A volte si ha a che fare con gli spazi nei dati, con o senza altri caratteri , anche se l'idea di utilizzare Null è migliore, ma non sempre utilizzabile. ho incontrato la situazione descritta e risolto in questo modo:

... dove ('>' + @space + '< ') <> ('>' + @ space2 + '<')

Ovviamente non si farebbe questa grande quantità di dati ma funziona veloce e facile per alcune centinaia di linee ...

Herbert

+1

La domanda è stata _why_ SQL server si è comportato come ha fatto, non come gestire un simile comportamento in generale. probabilmente jhale preferirebbe non modificare il suo codice di programma, ma solo la sua configurazione del server. –

0

Come distinti record su SELECT con campi char/varchar in SQL Server: esempio:

declare @mayvar as varchar(10) 

set @mayvar = 'data ' 

select mykey, myfield from mytable where myfield = @mayvar 

previsto

MyKey (int) | myfield (varchar10)

1 | 'Dati'

ottenuto

mykey | MyField

1 | "dati" 2 | 'Dati'

anche se scrivo select mykey, myfield from mytable where myfield = 'data' (senza vuoto finale) I ottenere gli stessi risultati.

come ho risolto? In questa modalità:

select mykey, myfield 
from mytable 
where myfield = @mayvar 
and DATALENGTH(isnull(myfield,'')) = DATALENGTH(@mayvar) 

e se c'è un indice su myfield, sarà utilizzato in ogni caso.

spero che sarà utile.