web-dev-qa-db-fra.com

La comparaison binaire 0x et 0x00 s'avère être égale sur SQL Server

Il semble que SQL Server considère que 0x et 0x00 valeurs égales:

SELECT CASE WHEN 0x = 0x00 THEN 1 ELSE 0 END

Cette sortie 1.

Comment puis-je obtenir un vrai comportement de comparaison binaire binaire? aussi, quelles sont les règles exactes sous lesquelles deux (var)binary Les valeurs sont considérées comme égales?

Notez également le comportement suivant:

--prints just one of the values
SELECT DISTINCT [Data]
FROM (VALUES (0x), (0x00), (0x0000)) x([Data])

--prints the obvious length values 1, 2 and 3
SELECT DATALENGTH([Data]) AS [DATALENGTH], LEN([Data]) AS [LEN]
FROM (VALUES (0x), (0x00), (0x0000)) x([Data])

Contexte de la question est que j'essaie de dédupliquer des données binaires. J'ai besoin de GROUP BY Données binaires, non seulement comparer deux valeurs. Je suis content d'avoir même remarqué ce problème.

Remarque, que HASHBYTES ne prend pas en charge les lecteurs. J'aimerais aussi trouver une solution plus simple.

5
usr

Je n'ai pas pu trouver ce comportement de comparaison spécifié nulle part dans Bol.

Mais l'élément de connexion comparaison d'égalité non valide pour les données varbinaires avec zéros rembourrés droit

Fondamentalement, la norme laisse la mise en œuvre pour traiter les chaînes qui ne diffèrent que par [traînant] 00 comme égal ou moins. Nous le traitons comme égaux.

L'élément Connect indique également que la présence de zéros traînants est le seul cas dans lequel SQL Server diffère du comportement de comparaison octet-byte-octet.

Afin de distinguer deux valeurs binaires dans SQL Server qui ne diffèrent que par le suivi 0x00 caractères Vous pouvez également ajouter DATALENGTH dans la comparaison comme indiqué dans votre question.

La raison de préférer DATALENGTH plutôt que LEN généralement ici est que ce dernier donne une extinction implicite à varchar, puis vous obtenez le problème avec des espaces de fin.

+-------------+--------------------+
| LEN(0x2020) | DATALENGTH(0x2020) |
+-------------+--------------------+
|           0 |                  2 |
+-------------+--------------------+

Bien que soit fonctionner dans votre cas d'utilisation.

3
Martin Smith

Fait intéressant, les deux valeurs 0x0 et 0x00 ne sont que des représentations de caractères différentes pour la même valeur stockée. Essayez d'exécuter l'extrait suivant pour prouver cela à vous-même.

DECLARE @foo    sql_variant
,  @bar         sql_variant
,  @bat         sql_variant

SET @foo = 0x0
SET @bar = 0x00
SET @bat = 0x00000000

SELECT 'foo' AS 'Var',  @foo                AS 'Value'
,  SQL_VARIANT_PROPERTY(@foo, 'BaseType')   AS 'BaseType'
,  SQL_VARIANT_PROPERTY(@foo, 'Precisionh') AS 'Precision'
,  SQL_VARIANT_PROPERTY(@foo, 'Scale')      AS 'Scale'
,  SQL_VARIANT_PROPERTY(@foo, 'MaxLength')  AS 'MaxLength'
UNION
SELECT 'bar' AS 'Var',  @bar
,  SQL_VARIANT_PROPERTY(@bar, 'BaseType')
,  SQL_VARIANT_PROPERTY(@bar, 'Precisionh')
,  SQL_VARIANT_PROPERTY(@bar, 'Scale')
,  SQL_VARIANT_PROPERTY(@bar, 'MaxLength')
UNION
SELECT 'bat' AS 'Var',  @bat
,  SQL_VARIANT_PROPERTY(@bat, 'BaseType')
,  SQL_VARIANT_PROPERTY(@bat, 'Precisionh')
,  SQL_VARIANT_PROPERTY(@bat, 'Scale')
,  SQL_VARIANT_PROPERTY(@bat, 'MaxLength')

SELECT
   CASE
      WHEN @foo = @bar
      THEN 'equal' 
      ELSE 'NOT EQUAL' 
   END AS TestResults

Je peux comprendre pourquoi le rembourrage zéro surprendrait les gens, mais cela a été le comportement par défaut depuis très longtemps, je suppose que je suis arrivé à y attendre.

-Patp

2
Pat Phelan