Leerzeichen kürzen (Leerzeichen, Tabulatoren, Zeilenumbrüche)

10

Ich arbeite mit SQL Server 2014 und muss Leerzeichen vom Anfang und Ende des Inhalts einer Spalte bereinigen, wobei Leerzeichen einfache Leerzeichen, Tabulatoren oder Zeilenumbrüche (sowohl \nals auch \r\n) sein können. z.B

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

und so weiter.

Ich konnte nur den ersten Fall mit erreichen

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

aber für die anderen Fälle funktioniert es nicht.

Giovanni Lovato
quelle

Antworten:

8

Für alle, die SQL Server 2017 oder höher verwenden

Sie können die integrierte TRIM -Funktion verwenden. Beispielsweise:

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

Beachten Sie, dass das Standardverhalten von darin TRIMbesteht, nur Leerzeichen zu entfernen. Um also auch die Tabulatoren und Zeilenumbrüche (CR + LFs) zu entfernen, müssen Sie die characters FROMKlausel angeben .

Außerdem habe ich NCHAR(0x09)für die Tabulatorzeichen in der @TestVariablen verwendet, damit der Beispielcode kopiert und eingefügt werden kann und die richtigen Zeichen beibehalten werden. Andernfalls werden Registerkarten beim Rendern dieser Seite in Leerzeichen umgewandelt.

Für alle, die SQL Server 2016 oder älter verwenden

Sie können eine Funktion entweder als SQLCLR Scalar UDF oder als T-SQL Inline TVF (iTVF) erstellen. Die T-SQL Inline TVF wäre wie folgt:

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

Und wie folgt laufen lassen:

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;

Kehrt zurück:

proof
----
~this 
              content~

Und Sie können das in einer UPDATEVerwendung verwenden 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

Wie eingangs erwähnt, ist dies auch über SQLCLR sehr einfach, da .NET eine Trim()Methode enthält, die genau die gewünschte Operation ausführt . Sie können entweder Code Ihr eigen zu nennen SqlString.Value.Trim(), oder Sie können einfach die kostenlose Version der Installation von SQL # Bibliothek (die ich erstellt, aber diese Funktion ist in der Free - Version) und die Nutzung entweder String_Trim (die nur Leerraum der Fall ist) oder String_TrimChars wo Sie geben die Zeichen ein, um sie von beiden Seiten zu trimmen (genau wie beim oben gezeigten iTVF).

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

Und es gibt genau die gleiche Zeichenfolge zurück, die oben in der iTVF-Beispielausgabe gezeigt wurde. Als skalare UDF würden Sie sie jedoch wie folgt verwenden UPDATE:

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

Eine der oben genannten Methoden sollte für die Verwendung in Millionen von Zeilen effizient sein. Inline-TVFs sind im Gegensatz zu TVFs mit mehreren Anweisungen und skalaren T-SQL-UDFs optimierbar. SQLCLR-skalare UDFs können möglicherweise in parallelen Plänen verwendet werden, sofern sie als gekennzeichnet sind IsDeterministic=trueund keinen der beiden DataAccess-Typen auf setzen Read(die Standardeinstellung für den Benutzer- und Systemdatenzugriff ist None), und beide Bedingungen sind erfüllt true für beide oben genannten SQLCLR-Funktionen.

Solomon Rutzky
quelle
4

Möglicherweise möchten Sie eine TVF (Tabellenwertfunktion) verwenden, um die fehlerhaften Zeichen vom Anfang und Ende Ihrer Daten zu entfernen.

Erstellen Sie eine Tabelle für Testdaten:

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

Erstellen Sie die 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

Führen Sie die TVF aus, um die Ergebnisse anzuzeigen:

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

Ergebnisse:

Geben Sie hier die Bildbeschreibung ein

Die TVF ruft sich rekursiv auf, bis am Anfang und am Ende der an die Funktion übergebenen Zeichenfolge keine störenden Zeichen mehr vorhanden sind. Es ist unwahrscheinlich, dass dies über eine große Anzahl von Zeilen hinweg gut funktioniert, aber es würde wahrscheinlich funktionieren, wenn Sie dies verwenden, um Daten zu reparieren, wenn sie in die Datenbank eingefügt werden.

Sie können dies in einer Update-Anweisung verwenden:

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


SELECT *
FROM dbo.TrimTest;

Ergebnisse (als Text):

Geben Sie hier die Bildbeschreibung ein

Max Vernon
quelle
Vielen Dank, Max. Leider muss ich eine große Anzahl von Zeilen (Millionen) in mehreren Tabellen bereinigen. Ich hoffte, dass eine Funktion in einer UPDATEAbfrage wie LTRIM/ verwendet werden kann RTRIM, etwas in den Zeilen UPDATE table t SET t.column = TRIM(t.column, CONCAT(CHAR(9), CHAR(10), CHAR(13)))einer TRIM( expression, charlist )Funktion, die eine Liste von Zeichen zum Trimmen akzeptiert wie viele Skriptsprachen haben.
Giovanni Lovato
Die Warnung, die ich darüber gegeben habe, "wahrscheinlich" funktioniert nicht gut in vielen Zeilen, kann ein Problem sein oder auch nicht. Wenn Sie dies nur einmal tun, ist dies möglicherweise kein Problem. Möglicherweise möchten Sie es in einer Umgebung ohne Produktion testen, damit Sie sehen können, wie lange es dauert.
Max Vernon
Ich werde meine Antwort aktualisieren, um zu zeigen, wie Sie dies in einer updateAnweisung verwenden würden.
Max Vernon
1

Ich hatte gerade ein Problem mit dieser speziellen Situation, ich musste jedes Feld mit Leerzeichen finden und bereinigen, aber ich fand 4 Arten von möglichen Leerzeichen in meinen Datenbankfeldern (Verweis auf ASCII-Codetabelle):

  • Horizontale Registerkarte (Zeichen (9))
  • Neue Zeile (char (10))
  • Vertikale Registerkarte (Zeichen (9))
  • Leerzeichen (char (32))

Vielleicht kann Ihnen diese Abfrage helfen.

UPDATE @TABLE SET @COLUMN = replace(replace(replace(replace(@COLUMN,CHAR(9),''),CHAR(10),''),CHAR(13),''),CHAR(32),'')
sami.almasagedi
quelle
Dadurch werden auch Leerzeichen aus der Mitte der Felder entfernt, nicht nur der Anfang und das Ende, wie in der Frage gestellt.
Colin 't Hart
Ja, Sie haben Recht, ich werde bearbeiten
sami.almasagedi
-1

Sie müssten das zweite Beispiel analysieren, da LTRIM / RTRIM nur Leerzeichen trimmt. Sie möchten tatsächlich kürzen, was SQL als Daten betrachtet (/ r, / t usw.). Wenn Sie die gesuchten Werte kennen, ersetzen Sie sie einfach durch REPLACE. Besser noch, schreiben Sie eine Funktion und rufen Sie sie auf.

Sozialer Exodus
quelle
-1

Wenn Sie möchten, nutzen Sie meine elegante Funktion:

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
quelle
1
Skalarwertfunktionen sind kaum elegant. Sie erzwingen die serielle Ausführung von Abfragen und werden einmal pro Zeile (nicht einmal pro Abfrage) ausgeführt. Sie sollten sich stattdessen Funktionen mit Inline-Tabellenwerten ansehen.
Erik Darling
-2

Die Verwendung der Funktion für große Datenmengen kann lange Ausführungszeiten dauern. Ich habe einen Datensatz von 8 Millionen Zeilen. Die Ausführung der Funktion dauerte mehr als 30 Minuten. replace(replace(replace(replace(@COLUMN,CHAR(9),''),CHAR(10),''),CHAR(13),''),CHAR(32),'')dauerte nur 5 sek. Vielen Dank an alle. Ich sehe dich @ sami.almasagedi und @Colin 't Hart

Abakus
quelle
Wie in der Antwort, die Sie wiederholen, wird das Problem dadurch nicht behoben, wenn Leerzeichen zwischen dem ersten und dem letzten Nicht-Leerzeichen beibehalten werden müssen. Geschwindigkeit ist nur dann nützlich, wenn sie die gewünschte Antwort liefert. Lesen Sie auch die Hinweise in der akzeptierten Antwort, wie Sie sicherstellen können, dass Funktionen eine solche Abfrage nicht verlangsamen.
RDFozz