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?

varchar s y la igualdad son espinosas en TSQL. La función LEN dice:

Devuelve el número de caracteres, en lugar de la cantidad de bytes, de la expresión de cadena dada, excluyendo los espacios en blanco finales .

DATALENGTH usar DATALENGTH para obtener un recuento de byte verdadero 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
  • Almohadilla 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 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

Sin embargo, cuidado con LIKE : no es simétrico: trata los espacios en blanco al final 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 

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:

Consulte Uso de datos de 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 cada carácter, reglas de comparación y 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 progtwigció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 objective).

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.

Encontré este artículo de blog que describe el comportamiento y explica por qué.

El estándar SQL requiere que las comparaciones de cadenas, efectivamente, rellenen la cadena más corta con caracteres espaciales. Esto conduce al sorprendente resultado de que N ” = N ” (la cadena vacía es igual a una cadena de uno o más caracteres espaciales) y más generalmente cualquier cadena es igual a otra cadena si difieren solo por espacios finales. Esto puede ser un problema en algunos contextos.

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

Hubo una pregunta similar hace un tiempo en la que busqué 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í.

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

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

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

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) obtengo 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.