Cadena vacía de SQL Server 2008 vs.Espacio
-
22-09-2019 - |
Pregunta
Me encontré con algo un poco extraño esta mañana y pensé en enviarlo para comentarlo.
¿Alguien puede explicar por qué la siguiente consulta SQL imprime "igual" cuando se ejecuta en SQL 2008?El nivel de compatibilidad de la base de datos está establecido en 100.
if '' = ' '
print 'equal'
else
print 'not equal'
Y esto devuelve 0:
select (LEN(' '))
Parece estar recortando automáticamente el espacio.No tengo idea de si este fue el caso en versiones anteriores de SQL Server y ya no tengo nadie para probarlo.
Me encontré con esto porque una consulta de producción arrojaba resultados incorrectos.No puedo encontrar este comportamiento documentado en ninguna parte.
¿Alguien tiene alguna información sobre esto?
Solución
varchar
s y la igualdad son espinosos en TSQL.El LEN
funcion dice:
Devuelve el número de caracteres, en lugar del número de bytes, de la expresión de cadena dada. excluyendo los espacios en blanco finales.
Necesitas usar DATALENGTH
para conseguir 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 determinar la igualdad de esta manera:
- Obtener una cadena más corta
- Almohadilla con espacios en blanco hasta que la longitud sea igual a la de la cuerda más larga
- comparar los dos
Es el paso intermedio el que está provocando resultados inesperados; después de ese paso, se comparan efectivamente los espacios en blanco con los espacios en blanco, por lo que se consideran iguales.
LIKE
se comporta mejor que =
en la situación de "espacios en blanco" porque no realiza un 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
aunque:no es simétrico:trata los espacios en blanco finales como significativos en el patrón (RHS) pero no en 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
Otros consejos
El operador = es T-SQL no es tanto "es igual a" como es "son la misma palabra / frase, de acuerdo con la intercalación de contexto de la expresión", y LEN es "el número de caracteres de la palabra / frase." No tratan el colaciones blancos de cola como parte de la palabra / frase anterior a ellos (aunque sí tratar líder espacios en blanco como parte de la cadena que preceden).
Si es necesario distinguir 'esto' de 'esto', no debe utilizar el "son la misma palabra o frase" operador porque 'esto' y 'esto' son la misma palabra.
Contribuir a la forma = obras es la idea de que el operador de cadena de la igualdad debe depender de sus contenidos argumentos y en el contexto de colación de la expresión, pero no debe depender de los tipos de los argumentos, si están ambos tipos de cadenas.
El concepto de lenguaje natural "son la misma palabra" no suele ser lo suficientemente precisas para poder ser capturado por un operador matemático como =, y no hay concepto de tipo cadena en lenguaje natural. Contexto (es decir, el cotejo) importa (y existe en lenguaje natural) y es parte de la historia, y las propiedades adicionales (algunos que parecen extravagantes) son parte de la definición de = con el fin de que sea bien definido en el mundo no natural de datos.
Sobre la cuestión de tipo, que no querría decir al cambio cuando se almacenan en diferentes tipos de cadenas. Por ejemplo, el tipo VARCHAR (10), CHAR (10), y CHAR (3) pueden todas las representaciones controlar la palabra 'cat', y? = 'Gato' debe dejar a decidir si un valor de cualquiera de estos tipos tiene la palabra 'gato' (con problemas de caja y acento determinados por la colación).
Respuesta al comentario de JohnFx:
Usando datos CHAR y VARCHAR en los libros en pantalla. Citando de esa página, el énfasis es mío:
Cada valor de datos char y varchar tiene una intercalación. colaciones definen atributos tales como los patrones de bits utilizados para representar cada carácter, reglas de comparación , y sensibilidad a la caja o la acentuación.
Estoy de acuerdo que podría ser más fácil de encontrar, pero está documentado.
A tener en cuenta, también, es que la semántica de SQL, donde = tiene que ver con los datos del mundo real y el contexto de la comparación (en contraposición a algo acerca de los bits almacenados en el ordenador) ha sido parte de SQL durante mucho hora. La premisa de los RDBMS y SQL es la representación fiel de los datos del mundo real, por lo tanto, su apoyo a las intercalaciones muchos años antes que las ideas similares (como CultureInfo) entraron en el reino de Algol-como idiomas. La premisa de estos idiomas (al menos hasta hace muy poco) era en la ingeniería de resolución de problemas, no la gestión de los datos empresariales. (Recientemente, el uso de las lenguas 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 businessy.)
En mi opinión, no es justo criticar SQL por ser diferente de "la mayoría de los lenguajes de programación." SQL fue diseñado para apoyar un marco para el modelado de datos de negocio que es muy diferente de la ingeniería, por lo que el idioma es diferente (y mejor para su objetivo).
Heck, cuando se especificó SQL en primer lugar, algunos idiomas no tenía ningún tipo incorporado cadena. Y en algunos idiomas Aún así, el operador de igualdad entre las cuerdas no se puede comparar los datos de caracteres en absoluto, pero compara las referencias! No me sorprendería si en una o dos décadas, la idea de que == es dependiente de la cultura se convierte en la norma.
el blog artículo que describe el comportamiento y explica por qué.
El estándar SQL requiere que la cadena comparaciones, efectivamente, la almohadilla cadena más corta con caracteres de espacio. Esto nos lleva al resultado sorprendente que N '' = N'' (la cadena vacía es igual a una cadena de uno o más de espacio caracteres) y más generalmente cualquier cadena es igual a otra cadena si difieren sólo por los espacios finales. Esta puede ser un problema en algunos contextos.
Más información también está disponible en MSKB316626
Hubo una pregunta similar hace un tiempo en el que daba a un problema similar aquí
En lugar de LEN ( ' '), el uso DATALENGTH ('') -. Que le da el valor correcto
Las soluciones eran de utilizar una cláusula LIKE como se explica en mi respuesta en ese país, y / o pueden incluir una segunda condición en la cláusula WHERE para comprobar DATALENGTH también.
Tiene una lectura de esa pregunta y enlaces en ese país.
Para comparar un valor de un espacio literal, es posible también utilizar esta técnica como una alternativa a la declaración como:
IF ASCII('') = 32 PRINT 'equal' ELSE PRINT 'not equal'
A veces uno tiene que lidiar con los espacios en los datos, con o sin otros personajes, a pesar de que la idea de utilizar Null es mejor - pero no siempre utilizable. Lo hice correr en la situación descrita y resuelto de esta manera:
... donde ( '>' + @Space + '<') <> ( '>' + @ space2 + '<')
Por supuesto que no haría eso FPR gran cantidad de datos, pero funciona rápida y fácil para algunos cientos de líneas ...
Herbert
Como en registros distintos de selección 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
esperada
mykey (int) | myfield (varchar10)
1 | 'datos'
obtenido
mykey | myfield
1 | 'datos' 2 | 'datos'
incluso si escribo
select mykey, myfield from mytable where myfield = 'data'
(sin blanco final)
Consigo los mismos resultados.
cómo resuelve? En este modo:
select mykey, myfield
from mytable
where myfield = @mayvar
and DATALENGTH(isnull(myfield,'')) = DATALENGTH(@mayvar)
y si hay un índice en myfield, que va a utilizar en cada caso.
espero que será útil.
Otra forma es poner de nuevo en un estado que el espacio tiene valor. por ejemplo: reemplazar el espacio con un personaje conocido como el _
if REPLACE('hello',' ','_') = REPLACE('hello ',' ','_')
print 'equal'
else
print 'not equal'
devuelve: no es igual
No es ideal, y probablemente lento, pero es otra forma rápida hacia adelante cuando se necesitan con urgencia.