2010-10-15 4 views
9

Se memorizzo intenzionalmente spazi finali in una colonna VARCHAR, come posso forzare SQL Server a visualizzare i dati come mancata corrispondenza?Come posso rendere SQL Server restituire FALSE per confrontare varchars con e senza spazi finali?

SELECT 'foo' WHERE 'bar' = 'bar ' 

ho provato:

SELECT 'foo' WHERE LEN('bar') = LEN('bar ') 

Un metodo che ho visto galleggiato è quello di aggiungere un carattere specifico alla fine di ogni stringa poi striscia di nuovo fuori per la mia presentazione ... ma questo sembra piuttosto stupido.

C'è un metodo che ho trascurato?

Ho notato che esso non si applica a leader spazi così forse ho eseguito una funzione che inverte l'ordine carattere prima del confronto .... problema è che questo rende la query unSARGable ....

+0

Confronta anche la lunghezza? – SteveCav

+1

Come ho detto, confronto la lunghezza con gli stessi risultati. – Matthew

+0

L'ho incontrato l'altro giorno e l'unica cosa che ho trovato è stata la stringa append. –

risposta

6

SQL Server li vede in modo diverso.

LEN (Transact-SQL)
Returns the number of characters of the specified string expression, excluding trailing blanks.
To return the number of bytes used to represent an expression, use the DATALENGTH function

Inoltre è meglio essere consapevoli che SQL Server follows ANSI/ISO SQL-92 padding the character strings used in comparisons in modo che il loro match lunghezze davanti a loro a confronto.

Aggiornamento (successivo):
Ho eliminato il mio codice utilizzando LIKE (che non riempie gli spazi durante il confronto) e DATALENGTH() poiché l'impegno non è un confronto improprio delle stringhe.

In ogni caso, questa domanda è duplicata di alcuni precedenti in SO, contenenti risposte, ad esempio, this one. E ce ne sono altri (non li ho letti per le risposte esotiche) this, this e altri. La ricerca trova dozzine in SO e in milioni in internet. Non so come sia stato possibile perdere la risposta.

+0

Penso che 'DATALENGTH' funzionerà bene per me qui, o memorizzerò un' DATALENGTH' calcolato in una colonna per renderlo più veloce a scapito di un byte. – Matthew

3

Come hai detto tu, non penso ci siano molte opzioni. Gli unici due che potrei venire con sono stati questi:

DECLARE @x nvarchar(50) 
DECLARE @y nvarchar(50) 
SET @x = 'CAT  ' 
SET @y = 'CAT' 

SELECT 1 WHERE len(@x + '_') = len(@y + '_') 

SELECT 1 WHERE reverse(@x) = reverse(@y) 

EDIT

Il pensiero di un terzo:

SELECT 1 WHERE REPLACE(@x, ' ', '_') = REPLACE(@y, ' ', '_') 

E quarto, supponendo che si sta su SQL 2005 +

SELECT 1 WHERE QUOTENAME(@x) = QUOTENAME(@y) 

Personalmente, mi piace l'idea di reverse st, ma tutto dipende da quale si comporta meglio per te.

+0

+1 per il REVERSE, ma il LEN (foo + un po 'di char) sarebbe probabilmente il più performante! –

+0

@ p.campbell - interessante, grazie per averlo indicato! – LittleBobbyTables

+1

Tutti aspirano alla velocità. Semplicemente non c'è una buona soluzione. – TomTom

1

Ho solo due suggerimenti. Uno sarebbe quello di rivisitare il design che richiede di memorizzare spazi finali - sono sempre un problema da affrontare in SQL.

Il secondo (dati i commenti di SARG) sarebbe quello di aggiungere una colonna calcolata alla tabella che memorizza la lunghezza e aggiungere questa colonna agli indici appropriati. In questo modo, almeno, il confronto della lunghezza dovrebbe essere in grado di SARG.

+0

Questa domanda avrebbe dovuto essere chiusa come dupe. È stato esaurientemente e ripetutamente risposto in SO prima, incluso il design (troncando gli spazi finali), che avevo fornito nel mio aggiornamento, http://stackoverflow.com/questions/1146280/is-it-good-practice-to-trim-whitespace -leading-and-trailing-when-select-inse –

+1

@ vgv8 - Penso che questo sia sottilmente differente, in quanto l'OP sembra voler dire che devono memorizzare gli spazi bianchi finali e che è significativo. –

2

si potrebbe provare somethign come questo:

declare @a varchar(10), @b varchar(10) 
set @a='foo' 
set @b='foo ' 

select @a, @b, DATALENGTH(@a), DATALENGTH(@b) 
+1

Sì. 'SELECT * FROM YourTable WHERE col = @searchterm e DATALENGTH (col) = DATALENGTH (@searchterm)' dovrebbe essere ancora ragionevolmente sargabile. –

+0

è fantastico martin, stavo cercando di pensare a un modo per farlo. – DForck42

1

Dopo un po 'di ricerca la soluzione più semplice che ho trovato era in Anthony Bloesch WebLog.

Basta aggiungere del testo (un char è sufficiente) alla fine dei dati (append)

SELECT 'foo' WHERE 'bar' + 'BOGUS_TXT' = 'bar ' + 'BOGUS_TXT' 

funziona anche per 'DOVE IN'

SELECT <columnA> 
FROM <tableA> 
WHERE <columnA> + 'BOGUS_TXT' in (SELECT <columnB> + 'BOGUS_TXT' FROM <tableB>) 
0

L'approccio che sto progettando da utilizzare è utilizzare un confronto normale che dovrebbe essere indicizzabile ("sargable") integrato da uno DATALENGTH (poiché LEN ignora gli spazi bianchi). Si sarebbe simile a questa:

DECLARE @testValue VARCHAR(MAX) = 'x'; 

SELECT t.Id, t.Value 
FROM dbo.MyTable t 
WHERE 1=1 
AND (t.Value = @testValue AND DATALENGTH(t.Value) = DATALENGTH(@testValue)) 

Spetta al Query Optimizer per decidere l'ordine dei filtri, ma dovrebbe scegliere di utilizzare un indice per la ricerca dei dati, se questo ha un senso per la tavola in fase di test e poi filtrare ulteriormente il risultato rimanente per lunghezza con le operazioni scalari più costose. Tuttavia, come indicato da un'altra risposta, sarebbe meglio evitare del tutto queste operazioni scalari utilizzando e indicizzando la colonna calcolata. Il metodo presentato qui potrebbe avere senso se non si ha alcun controllo sullo schema o se si desidera evitare il lavoro/la complicazione della configurazione delle colonne calcolate è considerato più costoso di avere prestazioni peggiori.