Cắt khoảng trắng (dấu cách, tab, dòng mới)


10

Tôi đang sử dụng SQL Server 2014 và tôi cần xóa khoảng trắng từ đầu và cuối nội dung của cột, trong đó khoảng trắng có thể là khoảng trắng, tab hoặc dòng mới (cả \n\r\n); ví dụ

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

và như thế.

Tôi chỉ có thể đạt được trường hợp đầu tiên với

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

nhưng đối với các trường hợp khác thì nó không hoạt động.

Câu trả lời:


8

Dành cho ai sử dụng SQL Server 2017 trở lên

bạn có thể sử dụng chức năng tích hợp TRIM . Ví dụ:

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

Xin lưu ý rằng hành vi mặc định của TRIMlà chỉ xóa các khoảng trắng, do đó, để xóa các tab và dòng mới (CR + LFs), bạn cần chỉ định characters FROMmệnh đề.

Ngoài ra, tôi đã sử dụng NCHAR(0x09)cho các ký tự tab trong @Testbiến để mã ví dụ có thể được sao chép và dán và giữ lại các ký tự chính xác. Mặt khác, các tab được chuyển đổi thành khoảng trắng khi trang này được hiển thị.

Đối với bất kỳ ai sử dụng SQL Server 2016 trở lên

Bạn có thể tạo một hàm, dưới dạng UDF vô hướng SQLCLR hoặc TVF nội tuyến T-SQL (iTVF). TVF nội tuyến T-SQL sẽ như sau:

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

Và chạy nó như sau:

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;

Trả về:

proof
----
~this 
              content~

Và bạn có thể sử dụng nó trong việc UPDATEsử dụng 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

Như đã đề cập ở phần đầu, điều này cũng thực sự dễ dàng thông qua SQLCLR vì .NET bao gồm một Trim()phương thức thực hiện chính xác thao tác bạn muốn. Bạn có thể mã của bạn riêng để gọi SqlString.Value.Trim(), hoặc bạn chỉ có thể cài đặt phiên bản miễn phí của SQL # thư viện (mà tôi tạo ra, nhưng chức năng này trong phiên bản miễn phí) và sử dụng một trong hai String_Trim (mà không gian chỉ trắng) hoặc String_TrimChars nơi bạn chuyển các ký tự để cắt từ cả hai phía (giống như iTVF được hiển thị ở trên).

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

Và nó trả về chuỗi chính xác như được hiển thị ở trên trong đầu ra ví dụ iTVF. Nhưng là một UDF vô hướng, bạn sẽ sử dụng nó như sau trong UPDATE:

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

Một trong những điều trên sẽ có hiệu quả để sử dụng trên hàng triệu hàng. TVF nội tuyến có thể tối ưu không giống như TVF đa câu lệnh và UDF vô hướng T-SQL. Và, các UDF vô hướng SQLCLR có tiềm năng được sử dụng trong các kế hoạch song song, miễn là chúng được đánh dấu IsDeterministic=truevà không đặt loại DataAccess thành Read(mặc định cho cả truy cập dữ liệu Người dùng và Hệ thống None) và cả hai điều kiện đó là đúng cho cả hai hàm SQLCLR đã lưu ý ở trên.


4

Bạn có thể muốn xem xét sử dụng TVF (chức năng có giá trị bảng) để xóa các ký tự vi phạm khỏi đầu và cuối dữ liệu của bạn.

Tạo một bảng để chứa dữ liệu thử nghiệm:

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

Tạo 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

Chạy TVF để hiển thị kết quả:

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

Các kết quả:

nhập mô tả hình ảnh ở đây

TVF tự gọi đệ quy cho đến khi không còn ký tự vi phạm nào ở đầu và cuối chuỗi được truyền vào hàm. Điều này không có khả năng thực hiện tốt trên một số lượng lớn các hàng, nhưng có thể sẽ hoạt động tốt nếu bạn đang sử dụng điều này để sửa dữ liệu khi nó được chèn vào cơ sở dữ liệu.

Bạn có thể sử dụng điều này trong một tuyên bố cập nhật:

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


SELECT *
FROM dbo.TrimTest;

Kết quả (dưới dạng văn bản):

nhập mô tả hình ảnh ở đây


Cảm ơn Max, thật không may, tôi phải dọn sạch một số lượng lớn hàng triệu (hàng triệu) trong nhiều bảng, tôi hy vọng trong một số hàm sẽ được sử dụng trong một UPDATEtruy vấn như LTRIM/ RTRIM, một cái gì đó trong một dòng UPDATE table t SET t.column = TRIM(t.column, CONCAT(CHAR(9), CHAR(10), CHAR(13)))TRIM( expression, charlist )chức năng chấp nhận danh sách các ký tự để cắt giống như nhiều ngôn ngữ scripting có.
Giovanni Lovato

Cảnh báo tôi đã đưa ra về nó "có thể" không hoạt động tốt trên nhiều hàng có thể hoặc không thể là một vấn đề. Nếu bạn đang làm điều này chỉ một lần, thì nó có thể không thành vấn đề. Bạn có thể muốn thử nghiệm nó trong một môi trường phi sản xuất để bạn có thể thấy nó mất bao lâu.
Max Vernon

Tôi sẽ cập nhật câu trả lời của mình để cho thấy cách bạn sử dụng điều này trong một updatetuyên bố.
Max Vernon

1

Tôi chỉ gặp vấn đề với tình huống cụ thể này, tôi cần tìm và dọn sạch mọi trường có khoảng trắng, nhưng tôi đã tìm thấy 4 loại khoảng trắng sở hữu trong các trường cơ sở dữ liệu của mình (Tham khảo bảng mã ASCII):

  • Tab ngang (char (9))
  • Dòng mới (char (10))
  • Tab dọc (char (9))
  • Không gian (char (32))

Có lẽ truy vấn này có thể giúp bạn.

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

Điều này làm sạch khoảng trắng từ giữa các trường quá, không chỉ bắt đầu và kết thúc như được hỏi trong câu hỏi.
Colin 't Hart

Vâng, bạn đã đúng, tôi sẽ chỉnh sửa
sami.almasagedi

-1

Bạn sẽ phải phân tích ví dụ thứ hai vì LTRIM / RTRIM chỉ cắt các khoảng trắng. Bạn thực sự muốn cắt bớt những gì SQL xem xét dữ liệu (/ r, / t, v.v.). Nếu bạn biết các giá trị bạn đang tìm kiếm, chỉ cần sử dụng REPLACE để thay thế chúng. Tốt hơn nữa, viết một chức năng và gọi nó.


-1

Nếu bạn thích, sử dụng chức năng thanh lịch của tôi:

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
Hàm vô hướng có giá trị hầu như không thanh lịch. Họ buộc các truy vấn chạy ser seri và thực hiện một lần trên mỗi hàng (không phải một lần cho mỗi truy vấn). Bạn nên nhìn vào các hàm nội tuyến có giá trị thay thế.
Erik Darling

-2

Sử dụng chức năng trên dữ liệu lớn có thể mất nhiều thời gian thực hiện. Tôi có một bộ dữ liệu gồm 8 triệu hàng, sử dụng hàm mất hơn 30 phút để thực thi. replace(replace(replace(replace(@COLUMN,CHAR(9),''),CHAR(10),''),CHAR(13),''),CHAR(32),'')chỉ mất 5 giây. Cảm ơn tất cả. Tôi thấy bạn @ sami.almasagedi và @Colin 't Hart


Như trong câu trả lời bạn đang lặp lại, điều này không giải quyết được vấn đề nếu khoảng trắng giữa các ký tự không phải khoảng trắng đầu tiên và cuối cùng phải được giữ lại. Tốc độ chỉ hữu ích khi nó dẫn đến câu trả lời mong muốn. Ngoài ra - xem các ghi chú trong câu trả lời được chấp nhận về cách đảm bảo các chức năng không làm chậm truy vấn như thế này.
RDFozz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.