¡Hola, chicos!
En este artículo me gustaría compartir una situación que suele ocurrir de vez en cuando en mi día a día como consultor, atendiendo a varios clientes y diferentes entornos, y que volvió a ocurrir el viernes, que es cuando hay “cosas raras” en cadenas y tablas (en alusión a la serie “Stranger Things”), que es la ocurrencia de personajes no visibles.
Después de ayudar a un consultor a identificar y resolver esto, pensé en crear una solución práctica para resolverlo más rápidamente la próxima vez y también ayudar a otras personas que puedan tener este problema en su vida diaria, que suele ocurrir especialmente en entornos que acaban de migrar o trabajan importando datos de varios sistemas, API o archivos.
Simulando el problema
Para demostrarle este problema, crearé una tabla muy simple y la llenaré con valores aleatorios y una línea fija que insertaré. Para hacer esto, usaré una función para generar valores aleatorios, que es fncRand, especialmente útil para su uso en funciones:
CREATE FUNCTION dbo.fncRand(
@Numero BIGINT
)
RETURNS BIGINT
AS
BEGIN
RETURN (ABS(CHECKSUM(PWDENCRYPT(N''))) / 2147483647.0) * @Numero
END
GO
Con esta función crearé la tabla e insertaré algunos valores aleatorios:
IF (OBJECT_ID('tempdb..#Teste') IS NOT NULL) DROP TABLE #Teste
CREATE TABLE #Teste (
Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
Nome VARCHAR(100) NOT NULL
)
DECLARE
@Contador INT = 1, @Total INT = 100,
@Contador2 INT = 1, @Total2 INT = 10,
@String VARCHAR(100)
WHILE(@Contador <= @Total)
BEGIN
SET @Contador2 = 1
SET @String = ''
WHILE(@Contador2 <= @Total2)
BEGIN
IF (@Contador2 <= 8)
SET @String += CHAR(65 + dbo.fncRand(25))
ELSE
SET @String += CHAR(dbo.fncRand(255))
SET @Contador2 += 1
END
INSERT INTO #Teste
VALUES(@String)
SET @Contador += 1
END
Ahora que hemos creado la tabla, demostraré lo que el cliente estaba intentando hacer, falló y se comunicó con nosotros para ayudarlo a comprender lo que estaba sucediendo:

Eh... La consulta de igualdad no arrojó nada, solo usó el operador LIKE. Copié y pegué la cadena devuelta por la consulta LIKE y todavía no funcionó. ¿Lo que está sucediendo?

Identificar el problema de los personajes ocultos
Bueno, probablemente debe haber caracteres ocultos en el medio de la cadena. Una forma muy rápida de saberlo es contar el número de caracteres de la cadena y analizar cuántos caracteres podemos ver:

Según el print de arriba, puedo ver 8 caracteres, pero la cadena tiene 10, según las funciones LEN y DATALENGTH, lo que probablemente indica que tenemos caracteres “invisibles” en nuestra columna o cadena, que podrían ser los caracteres de control de la tabla ASCII (recordando que la tabla varía según el idioma y el intercalado):

Para ayudar con esta identificación, creé la siguiente función que nos ayudará a identificar las líneas que tienen estos caracteres de control:
CREATE FUNCTION [dbo].[fncPossui_Caractere_Oculto](
@String VARCHAR(MAX)
)
RETURNS BIT
AS
BEGIN
RETURN (CASE WHEN PATINDEX('%[^ !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\^_`abcdefghijklmnopqrstuvwxyz|{}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ[[]%', REPLACE(@String, ']', '')) > 0 THEN 1 ELSE 0 END)
END
Su uso es muy sencillo y devuelve registros que tienen caracteres “no visibles”:

Identificar qué personajes están ocultos
Ahora nos falta identificar cuáles son estos personajes ocultos para valorar si vamos a intentar sustituirlos o no. Para facilitar esta tarea, creé la función fncMostra_Caracteres_Ocultos, que recibirá la cadena original y devolverá la posición y el código ASCII de cada carácter oculto en la cadena:
CREATE FUNCTION [dbo].[fncMostra_Caracteres_Ocultos](
@String VARCHAR(MAX)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE
@Result VARCHAR(MAX) = '',
@Contador INT = 1,
@Total INT,
@AdicionarBarra BIT = 0
SET @Total = LEN(@String)
WHILE(@Contador <= @Total)
BEGIN
IF (PATINDEX('%[^ !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\^_`abcdefghijklmnopqrstuvwxyz|{}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ[[]%', SUBSTRING(REPLACE(@String, ']', ''), @Contador, 1)) > 0)
BEGIN
SET @Result += (CASE WHEN @AdicionarBarra = 1 THEN ' | ' ELSE '' END) + 'Pos ' + CAST(@Contador AS VARCHAR(100)) + ': CHAR(' + CAST(ASCII(SUBSTRING(@String, @Contador, 1)) AS VARCHAR(5)) + ')'
SET @AdicionarBarra = 1
END
SET @Contador += 1
END
RETURN @Result
END
GO
Ejemplo de uso: análisis de una cadena

Ejemplo de uso: análisis de registros de una tabla

Devolver datos sin caracteres ocultos
Con la función fncMostra_Caracteres_Ocultos pude identificar qué caracteres de mi cadena están provocando que mi selección de igualdad no devuelva datos. Probaré si esto es cierto:

Si quieres devolver la información sin los caracteres “invisibles” de una forma aún más sencilla, puedes utilizar la función fncRemove_Caracteres_Hidden:
CREATE FUNCTION [dbo].[fncRemove_Caracteres_Ocultos](
@String VARCHAR(MAX)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE
@Result VARCHAR(MAX),
@StartingIndex INT = 0
WHILE (1 = 1)
BEGIN
SET @StartingIndex = PATINDEX('%[^ !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\^_`abcdefghijklmnopqrstuvwxyz|{}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ[[]%', REPLACE(@String, ']', ''))
IF (@StartingIndex <> 0)
SET @String = REPLACE(@String,SUBSTRING(@String, @StartingIndex,1),'')
ELSE
BREAK
END
SET @Result = REPLACE(@String,'|','')
RETURN @Result
END
GO
Ejemplo de uso de la función fncRemove_Caracteres_Hidden:

Nota importante: Como ocurre con todas las funciones UDF, estas funciones pueden provocar ralentizaciones cuando se utilizan en grandes volúmenes de datos. Si necesita utilizarlas frecuentemente en grandes volúmenes de datos, le sugiero implementar funciones usando SQLCLR, ya que generalmente ofrecen un rendimiento mucho mejor que las funciones UDF T-SQL, como ya expliqué en el artículo. SQL Server: comparación de rendimiento entre la función escalar y la función escalar CLR.
Bueno chicos, espero que les haya gustado este tip que les comparto y que estas funciones les puedan ser útiles en su vida diaria.
Un fuerte abrazo y nos vemos en el próximo artículo.

Comentários (0)
Carregando comentários…