83 votes

SQL Server 2008 : chaîne vide et espace

Je suis tombé sur quelque chose d'un peu étrange ce matin et j'ai pensé le soumettre à un commentaire.

Quelqu'un peut-il expliquer pourquoi la requête SQL suivante imprime 'equal' lorsqu'elle est exécutée avec SQL 2008 ? Le niveau de compatibilité de la base de données est fixé à 100.

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

Et ça renvoie 0 :

select (LEN(' '))

Il semble que l'espace se réduise automatiquement. Je n'ai aucune idée si c'était le cas dans les versions précédentes de SQL Server, et je n'en ai plus pour le tester.

J'ai rencontré ce problème parce qu'une requête de production renvoyait des résultats incorrects. Ce comportement n'est documenté nulle part.

Quelqu'un a-t-il des informations à ce sujet ?

2 votes

SQL 2005 : select len(' ') renvoie 0

1 votes

Il fait la même chose sur Sql Server 2000.

1 votes

C'est une question fascinante. Il semble que le résultat soit égal, quel que soit le nombre d'espaces que vous mettez dans l'une ou l'autre des chaînes, qu'elles correspondent ou non. Après plus d'expérimentation, j'ai remarqué qu'il effectue effectivement un RTRIM des deux côtés de l'opérateur d'égalité avant la comparaison. Il semble que vous ayez obtenu une réponse sur la fonction LEN, mais je suis vraiment intéressé par une réponse plus approfondie que "les variables et l'égalité sont épineuses dans TSQ" pour la partie égalité de votre question.

90voto

butterchicken Points 4768

varchar et l'égalité sont épineux en TSQL. Le site LEN fonction dit :

Renvoie le nombre de caractères, plutôt que le nombre d'octets, de l'expression de chaîne donnée, sans les blancs de fin de ligne .

Vous devez utiliser DATALENGTH pour obtenir un vrai byte compte 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

Lorsqu'il s'agit de l'égalité des expressions, les deux chaînes de caractères sont comparées pour l'égalité comme ceci :

  • Obtenir une chaîne plus courte
  • Tampon avec vides jusqu'à ce que la longueur soit égale à celle de la chaîne la plus longue
  • Comparez les deux

C'est l'étape intermédiaire qui provoque des résultats inattendus. Après cette étape, vous comparez effectivement des espaces blancs à d'autres espaces blancs, ce qui fait qu'ils sont considérés comme égaux.

LIKE se comporte mieux que = dans le cas des "blancs", car il n'effectue pas de remplissage de blancs sur le motif que vous essayez de faire correspondre :

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

donnera eq alors que :

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

donnera ne

Attention à LIKE Cependant, il n'est pas symétrique : il traite les espaces blancs de fin de ligne comme significatifs dans le motif (RHS) mais pas dans l'expression de correspondance (LHS). L'exemple suivant est tiré de aquí :

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 votes

Belle réponse. Je n'avais pas remarqué cela dans la documentation de LEN. Mais ce n'est pas limité à LEN. Les fonctions RIGHT et LEFT ont un comportement similaire, mais elles ne sont pas documentées. Il semble que ce soit le littéral avec un espace qui pose problème. J'ai remarqué que cette fonction renvoie également égal : if '' = SPACE(1) print 'equal' else print 'not equal' Je ne suis pas vraiment intéressé par l'obtention de la longueur réelle, je me suis juste demandé pourquoi, lorsque je cherchais un espace dans une colonne, toutes les colonnes qui étaient des chaînes vides étaient renvoyées.

0 votes

Par ailleurs, vous avez bien compris l'intérêt de l'instruction LIKE. Je suppose que la morale de l'histoire est d'essayer de ne pas se mettre dans la position où vous devez comparer un espace et une chaîne vide.

2 votes

Le problème est plus important que la comparaison d'un espace à une chaîne vide. La comparaison de deux chaînes de caractères qui se terminent par un nombre différent d'espaces présente le même comportement.

19voto

Steve Kass Points 4738

L'opérateur = en T-SQL n'est pas tant "égal" que "sont le même mot/phrase, selon la collation du contexte de l'expression", et LEN est "le nombre de caractères dans le mot/phrase". Aucune collation ne traite les blancs de fin de chaîne comme faisant partie du mot/de la phrase qui les précède (bien qu'elles traitent les blancs de début de chaîne comme faisant partie de la chaîne qu'ils précèdent).

Si vous devez distinguer 'ceci' de 'ceci', vous ne devez pas utiliser l'opérateur "sont le même mot ou la même phrase" car 'ceci' et 'ceci' sont le même mot.

L'idée selon laquelle l'opérateur d'égalité des chaînes de caractères devrait dépendre du contenu de ses arguments et du contexte de collation de l'expression, mais ne devrait pas dépendre des types des arguments, s'ils sont tous deux des chaînes de caractères, contribue au fonctionnement de =.

Le concept en langage naturel de "ces mots sont les mêmes" n'est généralement pas assez précis pour pouvoir être capturé par un opérateur mathématique comme =, et il n'y a pas de concept de type de chaîne dans le langage naturel. Le contexte (c'est-à-dire la collation) est important (et existe dans le langage naturel) et fait partie de l'histoire, et des propriétés supplémentaires (certaines qui semblent bizarres) font partie de la définition de = afin de le rendre bien défini dans le monde non naturel des données.

En ce qui concerne la question du type, vous ne voudriez pas que les mots changent lorsqu'ils sont stockés dans des types de chaînes différents. Par exemple, les types VARCHAR(10), CHAR(10) et CHAR(3) peuvent tous contenir des représentations du mot 'cat', et ? = 'cat' devrait nous permettre de décider si une valeur de l'un de ces types contient le mot 'cat' (les questions de casse et d'accent étant déterminées par la collation).

Réponse au commentaire de JohnFx :

Ver Utilisation des données char et varchar dans Livres en ligne. Citation de cette page, c'est moi qui souligne :

Chaque valeur de données char et varchar possède une collation. Les collations définissent des attributs tels que les modèles binaires utilisés pour représenter chaque caractère, règles de comparaison et la sensibilité à la casse ou à l'accentuation.

Je reconnais qu'il pourrait être plus facile à trouver, mais il est documenté.

Il convient également de noter que la sémantique de SQL, où = est lié aux données du monde réel et au contexte de la comparaison (par opposition à quelque chose concernant les bits stockés sur l'ordinateur), fait partie de SQL depuis longtemps. Le principe des SGBDR et de SQL est la représentation fidèle des données du monde réel, d'où la prise en charge des collations bien des années avant que des idées similaires (comme CultureInfo) n'entrent dans le domaine des langages de type Algol. Le principe de ces langages (du moins jusqu'à très récemment) était la résolution de problèmes d'ingénierie, et non la gestion de données commerciales. (Récemment, l'utilisation de langages similaires dans des applications qui ne relèvent pas de l'ingénierie, comme la recherche, fait quelques percées, mais Java, C#, etc. se débattent toujours avec leurs racines non commerciales).

À mon avis, il n'est pas juste de reprocher à SQL d'être différent de "la plupart des langages de programmation". SQL a été conçu pour prendre en charge un cadre de modélisation des données d'entreprise qui est très différent de l'ingénierie, le langage est donc différent (et meilleur pour son objectif).

Lorsque SQL a été spécifié pour la première fois, certains langages n'avaient pas de type de chaîne intégré. Et dans certains langages encore, l'opérateur égal entre chaînes de caractères ne compare pas du tout des données de caractères, mais des références ! Je ne serais pas surpris si, dans une ou deux décennies, l'idée que == dépend de la culture devenait la norme.

0 votes

BOL décrit l'opérateur = de la manière suivante : "Compare l'égalité de deux expressions (un opérateur de comparaison)". Que ce comportement soit correct ou non, vous devez admettre qu'il est extrêmement déroutant et non standard en termes d'utilisation de cet opérateur dans la plupart des langages de programmation. MS devrait au moins ajouter un avertissement à la documentation sur ce comportement.

0 votes

@JohnFx : Voir ma réponse trop longue pour un commentaire dans ma réponse.

9voto

JohnFx Points 23761

J'ai trouvé ceci article de blog qui décrit le comportement et explique pourquoi.

La norme SQL exige que les chaînes de caractères les comparaisons de chaînes de caractères remplissent effectivement la chaîne la plus courte avec des caractères d'espacement. Cela conduit au résultat surprenant que N'' = N' ' (la chaîne vide est égale à une chaîne d'un ou plusieurs caractères ) et, plus généralement, toute chaîne est égale à une autre chaîne si elles ne diffèrent que par des espaces de fin de chaîne. Ce site peut être un problème dans certains contextes.

Plus d'informations également disponibles dans MSKB316626

0 votes

Merci. Je suis surpris que cela figure dans la norme. Je suis sûr que quelqu'un de beaucoup plus intelligent que moi avait une bonne raison de le faire.

0 votes

@John : vouliez-vous écrire != (non égal) dans votre commentaire ?

0 votes

La citation originale contenait une erreur que j'ai copiée directement. J'ai mis à jour la citation pour refléter ce que l'auteur original voulait dire.

5voto

AdaTheDev Points 53358

Il y a quelque temps, j'ai examiné une question similaire qui posait le même problème aquí

Au lieu de LEN(' ') utiliser DATALENGTH(' ') - qui vous donne la valeur correcte.

Les solutions consistaient à utiliser un LIKE comme expliqué dans ma réponse ici, et/ou inclure une 2ème condition dans la clause WHERE clause à vérifier DATALENGTH aussi.

Lisez cette question et les liens qui s'y trouvent.

3voto

David G Points 21

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X