2010-10-15 77 views
9

Si je stocke délibérément des espaces de fin dans une colonne VARCHAR, comment puis-je forcer SQL Server à voir les données comme incompatibles?Comment faire pour que SQL Server retourne FALSE pour comparer les varchar avec et sans espaces de fin?

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

J'ai essayé:

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

Une méthode que j'ai vu flottais est d'ajouter un caractère spécifique à la fin de chaque chaîne décaper puis en arrière pour ma présentation ... mais semble assez idiot.

Y a-t-il une méthode que j'ai oubliée?

Je l'ai remarqué qu'il ne concerne pas leader espaces alors peut-être je lance une fonction qui intervertit l'ordre de caractère avant le comparer .... problème est que cela rend la requête unSARGable ....

+0

Comparez également la longueur? – SteveCav

+1

Comme je l'ai dit, je compare la longueur avec les mêmes résultats. – Matthew

+0

J'ai couru à travers cela l'autre jour et la seule chose que j'ai pu trouver était la chaîne append également. –

Répondre

6

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

Aussi, vous mieux être conscient que SQL Server follows ANSI/ISO SQL-92 padding the character strings used in comparisons de sorte que leur match longueurs avant de les comparer.

Mise à jour (plus tard):
J'ai supprimé mon code à l'aide LIKE (qui ne espaces pad lors de la comparaison) et DATALENGTH() car il n'est pas un engagement foolprof comparaison des chaînes.

Quoi qu'il en soit, cette question est une copie de quelques-unes des précédentes dans SO, contenant des réponses, par exemple, this one. Et il y en a d'autres (je ne les ai pas lus pour des réponses exhaystives) this, this, et autres. La recherche trouve des dizaines de SO et zillions sur internet. Je ne sais pas comment il était possible de rater la réponse.

+0

Je pense que 'DATALENGTH' fonctionnera bien pour moi ici, ou stocker un' DATALENGTH' calculé dans une colonne pour le rendre plus rapide au détriment d'un octet. – Matthew

3

Comme vous l'avez dit, je ne pense pas qu'il y ait beaucoup d'options. Les deux seuls que je pouvais venir avec ces étaient:

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

Pensée d'un troisième:

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

Et un quatrième, en supposant que vous sur SQL 2005+

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

Personnellement, j'aime l'idée reverse l'être st, mais tout dépend de celui qui fonctionne le mieux pour vous. SQL Server les voit différemment.

+0

+1 pour le REVERSE, mais le LEN (foo + un peu de char) serait probablement le plus performant! –

+0

@ p.campbell - intéressant, merci de le signaler! – LittleBobbyTables

+1

Tous d'entre eux sucent la vitesse sage. Il n'y a simplement pas de bonne solution. – TomTom

1

J'ai seulement deux suggestions. L'un serait de revoir la conception qui vous oblige à stocker les espaces de fin - ils sont toujours difficiles à gérer en SQL. La seconde (étant donné vos commentaires SARG-capables) serait d'ajouter une colonne calculée dans la table qui stocke la longueur, et ajouter cette colonne aux index appropriés. De cette façon, au moins, la comparaison de longueur devrait être SARG-capable.

+0

Cette question aurait du être fermée comme dupe. Il a été répondu de manière exhaustive et répétée dans SO avant, y compris la conception (tronquer les espaces de fin), j'avais donné dans ma mise à jour, http://stackoverflow.com/questions/1146280/is-it-good-practice-to-trim-whitespace -leading-and-trailing-when-selection-inse –

+1

@ vgv8 - Je pense que celui-ci est subtilement différent, en ce sens que l'OP semble dire qu'ils doivent stocker l'espace de fin, et que c'est important. –

2

vous pouvez essayer somethign comme ceci:

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

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

Oui. 'SELECT * FROM YourTable où col = @searchterm et DATALENGTH (col) = DATALENGTH (@searchterm)' devrait toujours être raisonnablement sargable. –

+0

c'est génial Martin, j'essayais de penser à un moyen de le faire. – DForck42

1

Après une recherche la solution la plus simple que j'ai trouvé dans Anthony Bloesch WebLog.

Il suffit d'ajouter un texte (char suffit) à la fin des données (append)

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

travaille également pour 'OÙ'

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

L'approche que je suis la planification utiliser est d'utiliser une comparaison normale qui devrait être index-keyable ("sargable") complétée par un DATALENGTH (parce que LEN ignore les espaces). Il ressemblerait à ceci:

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)) 

Il est à l'optimiseur de requête pour décider de l'ordre des filtres, mais il doit choisir d'utiliser un index pour la recherche de données si cela est logique pour la table en cours de test et de plus filtrer le résultat restant par longueur avec les opérations scalaires les plus chères. Cependant, comme une autre réponse l'indique, il vaudrait mieux éviter complètement ces opérations scalaires en utilisant une colonne calculée indexée. La méthode présentée ici pourrait avoir du sens si vous n'avez aucun contrôle sur le schéma ou si vous voulez éviter le travail/la complication de la mise en place des colonnes calculées est considéré comme plus coûteux que d'avoir des performances moins bonnes.