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?

¿Fue útil?

Solución

varchars 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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top