Recortar espacios en blanco (espacios, pestañas, líneas nuevas)

10

Estoy en SQL Server 2014 y necesito limpiar los espacios en blanco desde el inicio y el final del contenido de una columna, donde los espacios en blanco pueden ser espacios simples, pestañas o líneas nuevas (ambos \ny \r\n); p.ej

'    this content    '                          should become 'this content'
'  \r\n   \t\t\t this \r\n content \t  \r\n   ' should become 'this \r\n content'

y así.

Solo pude lograr el primer caso con

UPDATE table t SET t.column = LTRIM(RTRIM(t.column))

pero para los otros casos no funciona.

Giovanni Lovato
fuente

Respuestas:

8

Para cualquiera que use SQL Server 2017 o más reciente

puede usar la función incorporada TRIM . Por ejemplo:

DECLARE @Test NVARCHAR(4000);
SET @Test = N'  
    ' + NCHAR(0x09) + N'  ' + NCHAR(0x09) + N' this 
 ' + NCHAR(0x09) + NCHAR(0x09) + N'  content' + NCHAR(0x09) + NCHAR(0x09) + N'  
' + NCHAR(0x09) + N' ' + NCHAR(0x09) + NCHAR(0x09) + N'     ';

SELECT N'~'
        + TRIM(NCHAR(0x09) + NCHAR(0x20) + NCHAR(0x0D) + NCHAR(0x0A) FROM @Test)
        + N'~';

Tenga en cuenta que el comportamiento predeterminado de TRIMes eliminar solo espacios, por lo que para eliminar también las pestañas y las nuevas líneas (CR + LF), debe especificar la characters FROMcláusula.

Además, utilicé NCHAR(0x09)los caracteres de tabulación en la @Testvariable para que el código de ejemplo se pueda copiar y pegar y retener los caracteres correctos. De lo contrario, las pestañas se convierten en espacios cuando se representa esta página.

Para cualquiera que use SQL Server 2016 o anterior

Puede crear una función, ya sea como SQLCLR Scalar UDF o T-SQL Inline TVF (iTVF). El TVF en línea T-SQL sería el siguiente:

CREATE
--ALTER
FUNCTION dbo.TrimChars(@OriginalString NVARCHAR(4000), @CharsToTrim NVARCHAR(50))
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN
WITH cte AS
(
  SELECT PATINDEX(N'%[^' + @CharsToTrim + N']%', @OriginalString) AS [FirstChar],
         PATINDEX(N'%[^' + @CharsToTrim + N']%', REVERSE(@OriginalString)) AS [LastChar],
        LEN(@OriginalString + N'~') - 1 AS [ActualLength]
)
SELECT cte.[ActualLength],
       [FirstChar],
       ((cte.[ActualLength] - [LastChar]) + 1) AS [LastChar],
       SUBSTRING(@OriginalString, [FirstChar],
                 ((cte.[ActualLength] - [LastChar]) - [FirstChar] + 2)) AS [FixedString]
FROM   cte;
GO

Y ejecutándolo de la siguiente manera:

DECLARE @Test NVARCHAR(4000);
SET @Test = N'  
    ' + NCHAR(0x09) + N'  ' + NCHAR(0x09) + N' this 
 ' + NCHAR(0x09) + NCHAR(0x09) + N'  content' + NCHAR(0x09) + NCHAR(0x09) + N'  
' + NCHAR(0x09) + N' ' + NCHAR(0x09) + NCHAR(0x09) + N'     ';

SELECT N'~' + tc.[FixedString] + N'~' AS [proof]
FROM   dbo.TrimChars(@Test, NCHAR(0x09) + NCHAR(0x20) + NCHAR(0x0D) + NCHAR(0x0A)) tc;

Devoluciones:

proof
----
~this 
              content~

Y puedes usar eso en un UPDATEuso CROSS APPLY:

UPDATE tbl
SET    tbl.[Column] = itvf.[FixedString]
FROM   SchemaName.TableName tbl
CROSS APPLY  dbo.TrimChars(tbl.[Column],
                           NCHAR(0x09) + NCHAR(0x20) + NCHAR(0x0D) + NCHAR(0x0A)) itvf

Como se mencionó al principio, esto también es realmente fácil a través de SQLCLR ya que .NET incluye un Trim()método que realiza exactamente la operación que desea. Puede codificar el suyo para llamar SqlString.Value.Trim(), o simplemente puede instalar la versión gratuita de la biblioteca SQL # (que creé, pero esta función está en la versión gratuita) y usar String_Trim (que solo hace espacio en blanco) o String_TrimChars donde Usted pasa los caracteres para recortar de ambos lados (al igual que el iTVF que se muestra arriba).

DECLARE @Test NVARCHAR(4000);
SET @Test = N'  
    ' + NCHAR(0x09) + N'  ' + NCHAR(0x09) + N' this 
 ' + NCHAR(0x09) + NCHAR(0x09) + N'  content' + NCHAR(0x09) + NCHAR(0x09) + N'  
' + NCHAR(0x09) + N' ' + NCHAR(0x09) + NCHAR(0x09) + N'     ';

SELECT N'~' + SQL#.String_Trim(@Test) + N'~' AS [proof];

Y devuelve exactamente la misma cadena que se muestra arriba en la salida de ejemplo iTVF. Pero al ser un UDF escalar, lo usaría de la siguiente manera en un UPDATE:

UPDATE tbl
SET    tbl.[Column] = SQL#.String_Trim(itvf.[Column])
FROM   SchemaName.TableName tbl

Cualquiera de los anteriores debe ser eficiente para usar en millones de filas. Los TVF en línea son optimizables a diferencia de los TVF de múltiples declaraciones y los UDF escalares T-SQL. Y, los UDF escalares SQLCLR tienen el potencial de ser utilizados en planes paralelos, siempre que estén marcados IsDeterministic=truey no configuren ningún tipo de DataAccess Read(el valor predeterminado para el acceso a datos del usuario y del sistema es None), y ambas condiciones son verdadero para las dos funciones SQLCLR mencionadas anteriormente.

Solomon Rutzky
fuente
4

Es posible que desee considerar el uso de una TVF (función de valor de tabla) para eliminar los caracteres ofensivos del inicio y el final de sus datos.

Cree una tabla para contener datos de prueba:

IF COALESCE(OBJECT_ID('dbo.TrimTest'), 0) <> 0
BEGIN
    DROP TABLE dbo.TrimTest;
END
CREATE TABLE dbo.TrimTest
(
    SampleData VARCHAR(50) NOT NULL
);

INSERT INTO dbo.TrimTest (SampleData)
SELECT CHAR(13) + CHAR(10) + CHAR(9) + 'this is ' + CHAR(13) + CHAR(10) + ' a test' + CHAR(13) + CHAR(10);
GO

Crea el TVF:

IF COALESCE(OBJECT_ID('dbo.StripCrLfTab'), 0) <> 0
BEGIN
    DROP FUNCTION dbo.StripCrLfTab;
END
GO
CREATE FUNCTION dbo.StripCrLfTab
(
    @val NVARCHAR(1000)
)
RETURNS @Results TABLE
(
    TrimmedVal NVARCHAR(1000) NULL
)
AS
BEGIN
    DECLARE @TrimmedVal NVARCHAR(1000);
    SET @TrimmedVal = CASE WHEN RIGHT(@val, 1) = CHAR(13) OR RIGHT(@val, 1) = CHAR(10) OR RIGHT(@val, 1) = CHAR(9)
            THEN LEFT(
                CASE WHEN LEFT(@val, 1) = CHAR(13) OR LEFT(@val, 1) = CHAR(10) OR LEFT(@val, 1) = CHAR(9)
                THEN RIGHT(@val, LEN(@val) - 1)
                ELSE @val
                END
                , LEN(@val) -1 )
            ELSE
                CASE WHEN LEFT(@val, 1) = CHAR(13) OR LEFT(@val, 1) = CHAR(10) OR LEFT(@val, 1) = CHAR(9)
                THEN RIGHT(@val, LEN(@val) - 1)
                ELSE @val
                END
            END;
    IF @TrimmedVal LIKE (CHAR(13) + '%')
        OR @TrimmedVal LIKE (CHAR(10) + '%')
        OR @TrimmedVal LIKE (CHAR(9) + '%')
        OR @TrimmedVal LIKE ('%' + CHAR(13))
        OR @TrimmedVal LIKE ('%' + CHAR(10))
        OR @TrimmedVal LIKE ('%' + CHAR(9))
        SELECT @TrimmedVal = tv.TrimmedVal
        FROM dbo.StripCrLfTab(@TrimmedVal) tv;
    INSERT INTO @Results (TrimmedVal)
    VALUES (@TrimmedVal);
    RETURN;
END;
GO

Ejecute TVF para mostrar los resultados:

SELECT tt.SampleData
    , stt.TrimmedVal
FROM dbo.TrimTest tt
CROSS APPLY dbo.StripCrLfTab(tt.SampleData) stt;

Resultados:

ingrese la descripción de la imagen aquí

El TVF se llama a sí mismo de manera recursiva hasta que no queden caracteres ofensivos restantes al comienzo y al final de la cadena que se pasa a la función. Es poco probable que funcione bien en una gran cantidad de filas, pero probablemente funcionaría bien si está utilizando esto para corregir los datos a medida que se insertan en la base de datos.

Puede usar esto en una declaración de actualización:

UPDATE dbo.TrimTest
SET TrimTest.SampleData = stt.TrimmedVal
FROM dbo.TrimTest tt
CROSS APPLY dbo.StripCrLfTab(tt.SampleData) stt;


SELECT *
FROM dbo.TrimTest;

Resultados (como texto):

ingrese la descripción de la imagen aquí

Max Vernon
fuente
Gracias Max, desafortunadamente tengo que limpiar una gran cantidad de filas (millones) en varias tablas, esperaba que alguna función se usara en una UPDATEconsulta como LTRIM/ RTRIM, algo en la línea de UPDATE table t SET t.column = TRIM(t.column, CONCAT(CHAR(9), CHAR(10), CHAR(13)))una TRIM( expression, charlist )función que acepta una lista de caracteres para recortar como muchos lenguajes de script tienen.
Giovanni Lovato
La advertencia que le di sobre "probablemente" que no funciona bien en muchas filas puede o no ser un problema. Si está haciendo esto solo una vez, puede que no sea un problema. Es posible que desee probarlo en un entorno que no sea de producción para poder ver cuánto tiempo lleva.
Max Vernon
Actualizaré mi respuesta para mostrar cómo usarías esto en una updatedeclaración.
Max Vernon
1

Acabo de tener un problema con esta situación particular, necesitaba encontrar y limpiar cada campo con espacios en blanco, pero encontré 4 tipos de espacios en blanco posibles en los campos de mi base de datos (Referencia a la tabla de códigos ASCII):

  • Pestaña horizontal (char (9))
  • Nueva línea (char (10))
  • Pestaña vertical (char (9))
  • Espacio (char (32))

Quizás esta consulta te pueda ayudar.

UPDATE @TABLE SET @COLUMN = replace(replace(replace(replace(@COLUMN,CHAR(9),''),CHAR(10),''),CHAR(13),''),CHAR(32),'')
sami.almasagedi
fuente
Esto limpia los espacios en blanco desde el medio de los campos también, no solo el inicio y el final como se hizo en la pregunta.
Colin 't Hart
Sí, tienes razón, editaré
sami.almasagedi
-1

Tendría que analizar el segundo ejemplo porque LTRIM / RTRIM solo recorta espacios. En realidad, desea recortar lo que SQL considera datos (/ r, / t, etc.). Si conoce los valores que está buscando, use REPLACE para reemplazarlos. Mejor aún, escriba una función y llámela.

Éxodo social
fuente
-1

Si lo desea, use mi elegante función:

CREATE FUNCTION s_Trim
(
    @s nvarchar(max)
)
RETURNS nvarchar(max)
AS
BEGIN
    -- Create comparators for LIKE operator
    DECLARE @whitespaces nvarchar(50) = CONCAT('[ ', CHAR(9), CHAR(10), CHAR(13), ']'); -- Concat chars that you consider as whitespaces
    DECLARE @leftComparator nvarchar(50) = @whitespaces + '%',
            @rightComparator nvarchar(50) = '%' + @whitespaces;
    -- LTRIM
    WHILE @s LIKE @leftComparator AND LEN(@s + 'x') > 1 SET @s = RIGHT(@s, LEN(@s + 'x') - 2)
    -- RTRIM
    WHILE @s LIKE @rightComparator AND LEN(@s + 'x') > 1 SET @s = LEFT(@s, LEN(@s + 'x') - 2)

    RETURN @s;
END
GO
meehocz
fuente
1
Las funciones valoradas escalares son apenas elegantes. Forzan que las consultas se ejecuten en serie y se ejecutan una vez por fila (no una vez por consulta). En su lugar, debe mirar las funciones con valores de tabla en línea.
Erik Darling
-2

Usar la función en datos grandes puede llevar mucho tiempo de ejecución. Tengo un conjunto de datos de 8 millones de filas, el uso de la función tardó más de 30 minutos en ejecutarse. replace(replace(replace(replace(@COLUMN,CHAR(9),''),CHAR(10),''),CHAR(13),''),CHAR(32),'')tomó solo 5 segundos Gracias a todos. Te veo @ sami.almasagedi y @Colin 't Hart

Ábaco
fuente
Como en la respuesta que está repitiendo, esto no resuelve el problema si se debe conservar el espacio en blanco entre el primer y el último carácter que no sea un espacio en blanco. La velocidad solo es útil cuando da como resultado la respuesta deseada. Además, vea las notas en la respuesta aceptada sobre cómo garantizar que las funciones no ralenticen una consulta como esta.
RDFozz