Potong spasi putih (spasi, tab, baris baru)

10

Saya menggunakan SQL Server 2014 dan saya perlu membersihkan spasi putih dari awal dan akhir konten kolom, di mana spasi putih bisa berupa ruang sederhana, tab, atau baris baru (keduanya \ndan \r\n); misalnya

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

dan seterusnya.

Saya hanya bisa mencapai kasus pertama dengan

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

tetapi untuk kasus lain itu tidak berhasil.

Giovanni Lovato
sumber

Jawaban:

8

Bagi siapa pun yang menggunakan SQL Server 2017 atau yang lebih baru

Anda dapat menggunakan fungsi bawaan TRIM . Sebagai contoh:

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

Harap perhatikan bahwa perilaku default TRIMadalah untuk menghapus hanya spasi, jadi untuk juga menghapus tab dan baris baru (CR + LF), Anda perlu menentukan characters FROMklausa.

Juga, saya menggunakan NCHAR(0x09)karakter tab dalam @Testvariabel sehingga kode contoh dapat disalin dan ditempelkan dan mempertahankan karakter yang benar. Jika tidak, tab dapat dikonversi menjadi spasi ketika halaman ini diberikan.

Untuk siapa saja yang menggunakan SQL Server 2016 atau lebih tua

Anda dapat membuat fungsi, baik sebagai SQLCLR Scalar UDF atau T-SQL Inline TVF (iTVF). T-SQL Inline TVF adalah sebagai berikut:

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

Dan menjalankannya sebagai berikut:

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;

Pengembalian:

proof
----
~this 
              content~

Dan Anda dapat menggunakannya dalam UPDATEmenggunakan 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

Seperti yang disebutkan di awal, ini juga sangat mudah melalui SQLCLR karena .NET mencakup Trim()metode yang tidak persis operasi yang Anda inginkan. Anda dapat mengkodekan panggilan Anda sendiri SqlString.Value.Trim(), atau Anda dapat menginstal versi gratis dari pustaka SQL # (yang saya buat, tetapi fungsi ini dalam versi Gratis) dan menggunakan String_Trim (yang hanya menggunakan spasi putih) atau String_TrimChars di mana Anda memasukkan karakter untuk memotong dari kedua sisi (seperti iTVF yang ditunjukkan di atas).

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

Dan mengembalikan string yang sama persis seperti yang ditunjukkan di atas dalam contoh keluaran iTVF. Tetapi sebagai skalar UDF, Anda akan menggunakannya sebagai berikut dalam UPDATE:

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

Salah satu di atas harus efisien untuk digunakan di jutaan baris. TVFs inline dapat dioptimalkan seperti TVFs multi-pernyataan dan UDF skalar T-SQL. Dan, SQLCLR Scalar UDFs memiliki potensi untuk digunakan dalam rencana paralel, selama mereka ditandai sebagai IsDeterministic=truedan tidak mengatur salah satu tipe DataAccess menjadi Read(default untuk akses data Pengguna dan Sistem None), dan kedua kondisi tersebut adalah berlaku untuk kedua fungsi SQLCLR yang disebutkan di atas.

Solomon Rutzky
sumber
4

Anda mungkin ingin mempertimbangkan untuk menggunakan TVF (fungsi bernilai tabel) untuk menghapus karakter yang menyinggung dari awal dan akhir data Anda.

Buat tabel untuk menyimpan data uji:

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

Buat 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

Jalankan TVF untuk menunjukkan hasil:

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

Hasil:

masukkan deskripsi gambar di sini

TVF menyebut dirinya secara rekursif sampai tidak ada karakter menyinggung yang tersisa pada awal dan akhir string yang dilewatkan ke fungsi. Ini tidak mungkin berkinerja baik pada sejumlah besar baris, tetapi mungkin akan berfungsi baik jika Anda menggunakan ini untuk memperbaiki data saat dimasukkan ke dalam database.

Anda dapat menggunakan ini dalam pernyataan pembaruan:

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


SELECT *
FROM dbo.TrimTest;

Hasil (sebagai teks):

masukkan deskripsi gambar di sini

Max Vernon
sumber
Terima kasih Max, sayangnya saya harus membersihkan sejumlah besar baris (jutaan) di beberapa tabel, saya berharap dalam beberapa fungsi untuk digunakan dalam UPDATEpermintaan seperti LTRIM/ RTRIM, sesuatu di baris UPDATE table t SET t.column = TRIM(t.column, CONCAT(CHAR(9), CHAR(10), CHAR(13)))dengan TRIM( expression, charlist )fungsi menerima daftar karakter untuk dipangkas seperti banyak bahasa scripting miliki.
Giovanni Lovato
Peringatan yang saya berikan tentang hal itu "mungkin" tidak bekerja dengan baik pada banyak baris mungkin atau mungkin tidak menjadi masalah. Jika Anda melakukan ini sekali saja, maka itu mungkin tidak menjadi masalah. Anda mungkin ingin mengujinya di lingkungan non-produksi sehingga Anda dapat melihat berapa lama.
Max Vernon
Saya akan memperbarui jawaban saya untuk menunjukkan bagaimana Anda akan menggunakan ini dalam sebuah updatepernyataan.
Max Vernon
1

Saya hanya punya masalah dengan situasi khusus ini, saya perlu menemukan dan membersihkan setiap bidang dengan spasi putih, tetapi saya menemukan 4 jenis ruang putih yang memungkinkan di bidang basis data saya (Referensi ke tabel kode ASCII):

  • Tab Horisontal (char (9))
  • Baris Baru (char (10))
  • Tab Vertikal (char (9))
  • Space (char (32))

Mungkin pertanyaan ini dapat membantu Anda.

UPDATE @TABLE SET @COLUMN = replace(replace(replace(replace(@COLUMN,CHAR(9),''),CHAR(10),''),CHAR(13),''),CHAR(32),'')
sami.almasagedi
sumber
Ini membersihkan spasi putih dari tengah bidang juga, bukan hanya awal dan akhir seperti yang ditanyakan dalam pertanyaan.
Colin 't Hart
Ya, Anda benar, saya akan mengedit
sami.almasagedi
-1

Anda harus melewatkan contoh kedua karena LTRIM / RTRIM hanya memangkas ruang. Anda sebenarnya ingin memangkas apa yang dianggap data SQL (/ r, / t, dll). Jika Anda tahu nilai yang Anda cari, gunakan saja REPLACE untuk menggantinya. Lebih baik lagi, tulis fungsi dan panggil saja.

Keluaran Sosial
sumber
-1

Jika Anda suka, gunakan fungsi elegan saya:

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
sumber
1
Fungsi skalar yang dihargai hampir tidak elegan. Mereka memaksa kueri untuk menjalankan serial, dan mengeksekusi sekali per baris (bukan sekali per kueri). Anda harus melihat fungsi-fungsi yang dihargai pada tabel sebaris.
Erik Darling
-2

Menggunakan fungsi pada data besar dapat memakan waktu lama. Saya memiliki dataset 8 juta baris, fungsi menggunakan waktu lebih dari 30 menit untuk dieksekusi. replace(replace(replace(replace(@COLUMN,CHAR(9),''),CHAR(10),''),CHAR(13),''),CHAR(32),'')hanya butuh 5 detik. Terima kasih semuanya. Aku melihatmu @ sami.almasagedi dan @Colin 't Hart

Abacus
sumber
Seperti dalam jawaban yang Anda ulangi, ini tidak menyelesaikan masalah jika spasi putih antara karakter non-spasi pertama dan terakhir harus dipertahankan. Kecepatan hanya berguna ketika menghasilkan jawaban yang diinginkan. Juga - lihat catatan dalam jawaban yang diterima tentang cara memastikan fungsi tidak memperlambat kueri seperti ini.
RDFozz