Question

Je suis tombé sur quelque chose d'un peu bizarre ce matin et je pensais que je le soumettre pour commentaire.

Quelqu'un peut-il expliquer pourquoi les impressions de la requête SQL suivantes Equal lorsqu'il est exécuté sur SQL 2008. Le niveau de compatibilité db est réglé sur 100.

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

Et retourne 0:

select (LEN(' '))

Il semble être automatique coupe l'espace. Je ne sais pas si cela a été le cas dans les versions précédentes de SQL Server, et je n'ai plus aucune autour de tester même pas.

Je suis tombé sur ce parce qu'une requête de production a été de retour des résultats incorrects. Je ne trouve pas ce comportement documenté nulle part.

Quelqu'un at-il des informations à ce sujet?

Était-ce utile?

La solution

varchars et l'égalité sont épineuses en TSQL. La fonction LEN dit:

  

Retourne le nombre de caractères, plutôt que le nombre d'octets, de l'expression de chaîne donnée, hors espaces de fin .

Vous devez utiliser DATALENGTH pour obtenir un vrai compte de byte des données en question. Si vous avez des données unicode, notez que la valeur que vous obtenez dans cette situation ne sera pas la même que la longueur du texte.

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

En ce qui concerne l'égalité des expressions, les deux chaînes sont comparées pour l'égalité comme ceci:

  • raccourcissent chaîne
  • Pad avec des blancs jusqu'à ce que la longueur est égale à celle de la chaîne plus longue
  • Comparer les deux

Il est l'étape intermédiaire qui provoque des résultats inattendus - après cette étape, vous comparez efficacement contre les espaces blancs -. Par conséquent, ils sont considérés comme égaux

LIKE se comporte mieux que = dans la situation des « blancs », car il ne fonctionne pas padding blanc sur le motif que vous essayez de faire correspondre:

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

Donnera eq alors:

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

Donnera ne

Attention avec LIKE bien: il est symétrique: il traite les espaces de fuite aussi importante dans le motif (D), mais pas l'expression de correspondance (G). Ce qui suit est extrait de :

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

Autres conseils

L'opérateur = est T-SQL est pas tant « égal », comme il est « sont le même mot / expression, selon le classement du contexte de l'expression, » et LEN est « le nombre de caractères dans le mot / phrase." Pas de queue blancs traitent l'collations dans le cadre du mot / expression qui les précède (bien qu'ils ne traitent les blancs dans le cadre de la chaîne, ils précèdent).

Si vous avez besoin de distinguer « ce » de « ceci », vous ne devriez pas utiliser le « sont le même mot ou expression » opérateur parce que « ceci » et « ceci » sont le même mot.

Contribuer à la façon dont = fonctionne est l'idée que l'opérateur chaîne de l'égalité doit dépendre du contenu de ses arguments et le contexte de la collation de l'expression, mais il ne devrait pas dépendre des types des arguments, si elles sont Les deux types de chaîne.

Le concept de langage naturel de « ce sont le même mot » est généralement pas suffisamment précis pour pouvoir être capturé par un opérateur mathématique comme =, et il n'y a pas de concept de type chaîne en langage naturel. Contexte (c.-à-collation) les questions (et existe en langage naturel) et fait partie de l'histoire, et d'autres propriétés (certains qui semblent bizarres) font partie de la définition de = afin de le rendre bien défini dans le monde naturel de données.

, vous ne voudriez pas que sur la question du type de mots pour changer quand ils sont stockés dans différents types de chaîne. Par exemple, les types VARCHAR (10), CHAR (10) et CHAR (3) peuvent tous contenir des représentations du mot 'chat', et? = « Cat » devrait nous permettre de décider si une valeur de l'un de ces types tient le mot « chat » (avec des questions de cas et accent déterminés par le classement).

Réponse au commentaire de JohnFx:

Voir Utiliser char et varchar données dans la documentation en ligne. Je cite cette page, Souligné par:

  

Chaque omble chevalier et la valeur de données varchar a une collation. définir les classements   des attributs tels que les motifs de bits utilisés pour représenter chaque caractère,    règles de comparaison , et la sensibilité au cas ou l'accentuation.

Je suis d'accord, il pourrait être plus facile à trouver, mais il est documenté.

A noter, aussi, est que la sémantique de SQL, où = doit faire avec les données du monde réel et le contexte de la comparaison (par opposition à quelque chose bits stockés sur l'ordinateur) a fait partie de SQL pour une longue temps. La prémisse de SGBDR et SQL est la représentation fidèle des données du monde réel, d'où son soutien à de nombreuses années avant que les classements des idées similaires (comme CultureInfo) sont entrés dans le domaine des langages comme Algol. La prémisse de ces langues (au moins jusqu'à très récemment) était la résolution des problèmes en ingénierie, et non la gestion des données d'entreprise. (Récemment, l'utilisation des langues similaires dans les applications non techniques telles que la recherche fait quelques incursions, mais Java, C #, etc. luttent toujours avec leurs racines non businessy.)

À mon avis, il est injuste de critiquer SQL pour être différent de « la plupart des langages de programmation. » SQL a été conçu pour supporter un cadre pour la modélisation des données d'affaires qui est très différent de l'ingénierie, le langage est différent (et mieux pour son objectif).

Heck, lorsque SQL a été spécifié, certaines langues ne disposaient pas de tout type de chaîne intégré. Et dans certaines langues encore, l'opérateur égal entre les chaînes ne se compare pas des données de caractère du tout, mais compare les références! Il ne me surprendrait pas si, dans une autre décennie ou deux, l'idée que == est dépendante de la culture devient la norme.

J'ai trouvé ce href="http://blogs.msdn.com/anthonybloesch/archive/2009/02/20/SQL-equality-and-trailing-spaces.aspx" article qui décrit le comportement et explique pourquoi.

  

Le standard SQL exige que la chaîne   des comparaisons, de manière efficace, le tampon   chaîne plus courte avec des espaces.   Cela conduit au résultat surprenant   que n « » = n » '(la chaîne vide   équivaut à une chaîne d'un ou plus d'espace   caractères) et plus généralement tout   chaîne est égale à une autre chaîne si elles   ne diffèrent que par des espaces de fuite. Cette   peut être un problème dans certains cas.

Plus d'informations disponibles MSKB316626

Il y avait une question similaire il y a un moment où je regardais un problème similaire ici

Au lieu de LEN ( ' '), l'utilisation DATALENGTH ('') -. Qui vous donne la valeur correcte

Les solutions devaient utiliser une clause LIKE comme expliqué dans ma réponse là-dedans, et / ou comprennent une 2ème condition dans la clause WHERE pour vérifier DATALENGTH aussi.

Avoir une lecture de cette question et des liens là-bas.

Pour comparer une valeur à un espace littéral, vous pouvez également utiliser cette technique comme une alternative à l'instruction LIKE:

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

Parfois, on a affaire avec des espaces dans les données, avec ou sans autres personnages, même si l'idée d'utiliser Null est mieux - mais pas toujours utilisable. J'ai couru dans la situation décrite et résolu de cette façon:

... où ( '>' + @space + '<') <> ( '>' + @ + space2 '<')

Bien sûr, vous ne voudriez pas faire fpr grande quantité de données, mais il fonctionne rapide et facile pour une centaine de lignes ...

Herbert

Comment dossiers distincts sur certains des champs char / varchar sur le serveur sql: exemple:

declare @mayvar as varchar(10)

set @mayvar = 'data '

select mykey, myfield from mytable where myfield = @mayvar

devrait

MyKey (int) | myfield (varchar10)

1 | 'Données'

obtenu

MyKey | myfield

1 | 'Données' 2 | 'Données'

même si j'écris select mykey, myfield from mytable where myfield = 'data' (sans blanc final) Je reçois les mêmes résultats.

comment je résolu? Dans ce mode:

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

et s'il y a un index sur myfield, il sera utilisé dans chaque cas.

J'espère que ce sera utile.

Une autre façon est de le remettre dans un état que l'espace a une valeur. par exemple: remplacer l'espace avec un personnage connu comme le _

if REPLACE('hello',' ','_') = REPLACE('hello ',' ','_')
    print 'equal'
else
    print 'not equal'

renvoie: non égal

Pas idéal, et probablement lent, mais il est un autre moyen rapide vers l'avant en cas de besoin rapidement.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top