Taglia spazi bianchi (spazi, tabulazioni, nuove righe)


10

Sono su SQL Server 2014 e ho bisogno di pulire gli spazi bianchi dall'inizio e alla fine del contenuto di una colonna, dove gli spazi bianchi potrebbero essere semplici spazi, schede o nuove righe (sia \ne che \r\n); per esempio

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

e così via.

Sono stato in grado di raggiungere solo il primo caso con

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

ma per gli altri casi non funziona.

Risposte:


8

Per chiunque utilizzi SQL Server 2017 o versioni successive

è possibile utilizzare la funzione integrata TRIM . Per esempio:

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'~';

Si noti che il comportamento predefinito di TRIMè di rimuovere solo gli spazi, quindi per rimuovere anche le schede e le nuove righe (CR + LF), è necessario specificare la characters FROMclausola.

Inoltre, ho usato NCHAR(0x09)i caratteri di tabulazione nella @Testvariabile in modo che il codice di esempio possa essere copiato e incollato e conservare i caratteri corretti. Altrimenti, le schede vengono convertite in spazi quando viene visualizzata questa pagina.

Per chiunque utilizzi SQL Server 2016 o versioni precedenti

È possibile creare una funzione, come UDF scalare SQLCLR o TVF in linea T-SQL (iTVF). Il T-SQL Inline TVF sarebbe il seguente:

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

E eseguendolo come segue:

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;

Ritorna:

proof
----
~this 
              content~

E puoi usarlo in un UPDATEutilizzo 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

Come accennato all'inizio, questo è anche molto semplice tramite SQLCLR poiché .NET include un Trim()metodo che esegue esattamente l'operazione desiderata. Puoi chiamare il tuo codice da chiamare SqlString.Value.Trim()oppure puoi semplicemente installare la versione gratuita della libreria SQL # (che ho creato, ma questa funzione è nella versione gratuita) e utilizzare String_Trim (che fa solo uno spazio bianco) o String_TrimChars dove passi i personaggi per tagliare da entrambi i lati (proprio come l'iTFF mostrato sopra).

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];

E restituisce la stessa stringa esatta mostrata sopra nell'output di esempio iTVF. Ma essendo un UDF scalare, lo useresti come segue in un UPDATE:

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

Uno dei precedenti dovrebbe essere efficiente per l'utilizzo su milioni di righe. I TVF in linea sono ottimizzabili a differenza dei TVF multiistruzione e degli UDF scalari T-SQL. Inoltre, gli UDF scalari SQLCLR hanno il potenziale per essere utilizzati in piani paralleli, purché siano contrassegnati come IsDeterministic=truee non impostino alcun tipo di DataAccess su Read(l'impostazione predefinita per l'accesso ai dati sia dell'utente che del sistema è None), ed entrambe le condizioni sono vero per entrambe le funzioni SQLCLR indicate sopra.


4

Potresti prendere in considerazione l'utilizzo di una TVF (funzione con valori di tabella) per rimuovere i caratteri offensivi dall'inizio e dalla fine dei tuoi dati.

Creare una tabella per contenere i dati di test:

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 il 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

Esegui TVF per mostrare i risultati:

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

risultati:

inserisci qui la descrizione dell'immagine

Il TVF si chiama ricorsivamente fino a quando non ci sono caratteri offensivi rimanenti all'inizio e alla fine della stringa passata nella funzione. È improbabile che ciò funzioni bene su un gran numero di righe, ma probabilmente funzionerebbe bene se lo si utilizza per correggere i dati quando vengono inseriti nel database.

Puoi usarlo in una dichiarazione di aggiornamento:

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


SELECT *
FROM dbo.TrimTest;

Risultati (come testo):

inserisci qui la descrizione dell'immagine


Grazie Max, purtroppo devo pulire una grande quantità di righe (milioni) in più tabelle, speravo in alcune funzioni da usare in una UPDATEquery come LTRIM/ RTRIM, qualcosa in linea UPDATE table t SET t.column = TRIM(t.column, CONCAT(CHAR(9), CHAR(10), CHAR(13)))con una TRIM( expression, charlist )funzione che accetta un elenco di caratteri da tagliare come hanno fatto molti linguaggi di scripting.
Giovanni Lovato,

L'avvertimento che ho dato a riguardo "probabilmente" non funziona bene su molte righe può o meno essere un problema. Se lo stai facendo solo una volta, potrebbe non essere un problema. Potresti voler testarlo in un ambiente non di produzione in modo da poter vedere quanto tempo impiega.
Max Vernon,

Aggiornerò la mia risposta per mostrare come lo useresti in una updatedichiarazione.
Max Vernon,

1

Ho appena avuto un problema con questa particolare situazione, avevo bisogno di trovare e pulire ogni campo con spazi bianchi, ma ho trovato 4 tipi di possibili spazi bianchi nei campi del mio database (riferimento alla tabella dei codici ASCII):

  • Scheda orizzontale (carattere (9))
  • New Line (char (10))
  • Scheda verticale (carattere (9))
  • Spazio (char (32))

Forse questa query può aiutarti.

UPDATE @TABLE SET @COLUMN = replace(replace(replace(replace(@COLUMN,CHAR(9),''),CHAR(10),''),CHAR(13),''),CHAR(32),'')

Questo pulisce anche gli spazi bianchi dalla metà dei campi, non solo l'inizio e la fine, come richiesto nella domanda.
Colin 't Hart,

Sì, hai ragione, io modificherò
sami.almasagedi il

-1

Dovresti analizzare il secondo esempio perché LTRIM / RTRIM taglia solo gli spazi. In realtà vuoi tagliare ciò che SQL considera i dati (/ r, / t, ecc.). Se conosci i valori che stai cercando, usa semplicemente REPLACE per sostituirli. Meglio ancora, scrivi una funzione e chiamala.


-1

Se ti piace, usa la mia elegante funzione:

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

1
Le funzioni con valori scalari non sono certo eleganti. Costringono le query a essere eseguite in serie ed eseguite una volta per riga (non una volta per query). Dovresti invece esaminare le funzioni con valori di tabella incorporate.
Erik Darling,

-2

L'uso della funzione su dati di grandi dimensioni può richiedere tempi di esecuzione lunghi. Ho un set di dati di 8 milioni di righe, utilizzando la funzione ha impiegato più di 30 minuti per l'esecuzione. replace(replace(replace(replace(@COLUMN,CHAR(9),''),CHAR(10),''),CHAR(13),''),CHAR(32),'')ci sono voluti solo 5 sec. Ringrazia tutti. Ti vedo @ sami.almasagedi e @Colin 't Hart


Come nella risposta che stai ripetendo, questo non risolve il problema se gli spazi bianchi tra il primo e l'ultimo carattere non bianco devono essere mantenuti. La velocità è utile solo quando si ottiene la risposta desiderata. Inoltre, vedere le note nella risposta accettata su come assicurarsi che le funzioni non rallentino una query come questa.
RDFozz,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.