Pregunta SQL Server 2008 Empty String vs. Space


Me encontré con algo un poco extraño esta mañana y pensé que lo enviaría para comentario.

¿Alguien puede explicar por qué la siguiente consulta SQL imprime 'igual' cuando se ejecuta en SQL 2008. El nivel de compatibilidad db está establecido en 100.

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

Y esto devuelve 0:

select (LEN(' '))

Parece que está recortando automáticamente el espacio. No tengo idea de si este fue el caso en las versiones anteriores de SQL Server, y ya no tengo ninguno para probarlo.

Me encontré con esto porque una consulta de producción estaba devolviendo resultados incorrectos. No puedo encontrar este comportamiento documentado en ninguna parte.

¿Alguien tiene información sobre esto?


75
2017-09-09 13:56


origen


Respuestas:


varcharsy la igualdad son espinosas en TSQL. los LEN función dice:

Devuelve la cantidad de caracteres, en lugar de la cantidad de bytes, de la expresión de cadena dada excluyendo espacios en blanco al final.

Tienes que usar DATALENGTH para obtener un verdadero byte recuento de los datos en cuestión. Si tiene datos Unicode, tenga en cuenta que el valor que obtenga en esta situación no será el mismo que la longitud del texto.

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

Cuando se trata de igualdad de expresiones, las dos cadenas se comparan para igualdad como esta:

  • Obtener una cadena más corta
  • Pad con espacios en blanco hasta que la longitud sea igual a la de una cadena más larga
  • Compara los dos

Es el paso intermedio el que está causando resultados inesperados: después de ese paso, comparas efectivamente el espacio en blanco con el espacio en blanco, por lo tanto se ven iguales.

LIKE se comporta mejor que = en la situación de "espacios en blanco" porque no realiza relleno en blanco en el patrón que intentaba hacer coincidir:

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

Daré eq mientras:

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

Daré ne

Cuidado con LIKE sin embargo: no es simétrico: trata los espacios en blanco al final como significativos en el patrón (RHS) pero no la expresión de coincidencia (LHS). Lo siguiente está tomado 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

78
2017-09-09 14:14



El operador = es que T-SQL no es tanto "igual" como "es la misma palabra / frase, según la intercalación del contexto de la expresión", y LEN es "el número de caracteres en la palabra / frase". Ninguna intercalación trata los espacios en blanco finales como parte de la palabra / frase que los precede (aunque sí tratan los espacios en blanco iniciales como parte de la cadena que preceden).

Si necesita distinguir 'esto' de 'esto', no debe usar el operador "son la misma palabra o frase" porque 'this' y 'this' son la misma palabra.

Contribuir a way = works es la idea de que el operador de igualdad de cadenas debe depender del contenido de sus argumentos y del contexto de intercalación de la expresión, pero no debe depender de los tipos de argumentos, si ambos son tipos de cadena .

El concepto del lenguaje natural de "estas son la misma palabra" no suele ser lo suficientemente preciso como para poder ser capturado por un operador matemático como =, y no hay ningún concepto de tipo de cuerda en el lenguaje natural. El contexto (es decir, la intercalación) importa (y existe en el lenguaje natural) y es parte de la historia, y las propiedades adicionales (algunas que parecen peculiares) son parte de la definición de = para hacerlo bien definido en el mundo antinatural de datos.

En el problema de tipo, no querría cambiar las palabras cuando están almacenadas en diferentes tipos de cadenas. Por ejemplo, los tipos VARCHAR (10), CHAR (10) y CHAR (3) pueden contener representaciones de la palabra 'cat', y? = 'cat' debería permitirnos decidir si un valor de cualquiera de estos tipos contiene la palabra 'cat' (con problemas de caso y acento determinados por la intercalación).

Respuesta al comentario de JohnFx:

Ver Usando datos char y varchar en Libros en línea. Citando de esa página, énfasis mío:

Cada valor de datos char y varchar tiene una intercalación. Las intercalaciones definen   atributos tales como los patrones de bits utilizados para representar a cada personaje,    reglas de comparacióny sensibilidad al caso o acento.

Estoy de acuerdo en que podría ser más fácil de encontrar, pero está documentado.

También vale la pena señalar que la semántica de SQL, donde = tiene que ver con los datos del mundo real y el contexto de la comparación (a diferencia de algo sobre los bits almacenados en la computadora) ha sido parte de SQL durante mucho tiempo. La premisa de RDBMS y SQL es la representación fiel de datos del mundo real, de ahí su apoyo para colaciones muchos años antes de que ideas similares (como CultureInfo) entraran en el reino de los lenguajes tipo Algol. La premisa de esos idiomas (al menos hasta hace muy poco) era la resolución de problemas en ingeniería, no la gestión de datos comerciales. (Recientemente, el uso de lenguajes similares en aplicaciones que no son de ingeniería como la búsqueda está haciendo algunas incursiones, pero Java, C #, y así sucesivamente todavía están luchando con sus raíces no comerciales).

En mi opinión, no es justo criticar a SQL por ser diferente de "la mayoría de los lenguajes de programación". SQL fue diseñado para soportar un marco para el modelado de datos de negocios que es muy diferente de la ingeniería, por lo que el lenguaje es diferente (y mejor para su objetivo).

Diablos, cuando SQL se especificó por primera vez, algunos idiomas no tenían ningún tipo de cadena incorporada. Y aún en algunos idiomas, el operador de igual entre cadenas no compara los datos de los caracteres, ¡pero compara las referencias! No me sorprendería si en una década o dos, la idea de que == depende de la cultura se convierte en la norma.


17
2017-09-09 15:20



encontré esto artículo de blog que describe el comportamiento y explica por qué.

El estándar SQL requiere esa cadena   comparaciones, efectivamente, rellene el   cadena más corta con caracteres espaciales.   Esto lleva al resultado sorprendente   que N '' = N '' (la cadena vacía   es igual a una cadena de uno o más espacios   personajes) y, en general, cualquier   string es igual a otra cadena si   difieren solo por espacios finales. Esta   puede ser un problema en algunos contextos.

Más información también disponible en MSKB316626


9
2017-09-09 15:03



Hubo una pregunta similar hace un tiempo en la que examiné un problema similar aquí

En lugar de LEN (''), use DATALENGTH ('') - eso le da el valor correcto.

Las soluciones fueron usar una cláusula LIKE como se explica en mi respuesta allí, y / o incluir una segunda condición en la cláusula WHERE para verificar DATALENGTH también.

Lea la pregunta y los enlaces allí.


4
2017-09-09 14:12



Para comparar un valor con un espacio literal, también puede usar esta técnica como alternativa a la declaración LIKE:

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

3
2018-02-24 20:03



A veces uno tiene que ocuparse de los espacios en los datos, con o sin otros personajes, aunque la idea de usar Null es mejor, pero no siempre utilizable. Me topé con la situación descrita y la resolví de esta manera:

... donde ('>' + @space + '<') <> ('>' + @ space2 + '<')

Por supuesto que no harías esa gran cantidad de datos pero funciona de manera rápida y fácil para cientos de líneas ...

Herbert


0
2018-01-16 14:37



Cómo distinguir registros en select con los campos char / varchar en el servidor sql: ejemplo:

declare @mayvar as varchar(10)

set @mayvar = 'data '

select mykey, myfield from mytable where myfield = @mayvar

esperado

mykey (int) | myfield (varchar10)

1 | 'datos'

adquirido

mykey | Mi campo

1 | 'datos' 2 | 'datos'

incluso si escribo select mykey, myfield from mytable where myfield = 'data' (sin final en blanco) Obtuve los mismos resultados.

¿Cómo lo resolví? En este modo:

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

y si hay un índice en myfield, se usará en cada caso.

Espero que sea util.


0
2018-04-14 15:45