修剪空白(空格,制表符,换行符)


10

我正在使用SQL Server 2014,需要从列内容的开头和结尾清除空格,其中空格可以是简单的空格,制表符或换行符(\n\r\n);例如

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

等等。

我只能用

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

但在其他情况下,则无效。

Answers:


8

对于使用SQL Server 2017或更高版本的任何人

您可以使用TRIM内置功能。例如:

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

请注意,的默认行为TRIM是仅删除空格,因此,为了同时删除制表符和换行符(CR + LF),您需要指定该characters FROM子句。

另外,我还使用NCHAR(0x09)@Test变量中的制表符,以便可以复制并粘贴示例代码并保留正确的字符。否则,呈现此页面时,选项卡将转换为空格。

对于使用SQL Server 2016或更早版本的任何人

您可以创建一个函数,作为SQLCLR标量UDF或T-SQL嵌入式TVF(iTVF)。T-SQL嵌入式TVF如下所示:

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

并如下运行:

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;

返回值:

proof
----
~this 
              content~

您可以在UPDATEusing中使用它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

如开头所述,通过SQLCLR确实很容易,因为.NET包含一种Trim()可以准确执行所需操作的方法。您可以编写自己的代码来调用SqlString.Value.Trim(),也可以仅安装SQL#库的免费版本(我创建了该版本,但此功能位于免费版本中),然后使用String_Trim(仅用于空白)或String_TrimChars,其中您传入的字符从两侧进行修剪(就像上面显示的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];

并且它返回与iTVF示例输出中上面显示的完全相同的字符串。但是作为标量UDF,您可以在UPDATE

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

以上任何一种对于在数百万行中使用都应该是有效的。与多语句TVF和T-SQL标量UDF不同,嵌入式TVF是可优化的。而且,只要将SQLCLR标量UDF标记为IsDeterministic=true且不将DataAccess的任何一种设置为Read(用户和系统数据访问的默认值为None),并且这两种条件均为,则它们就有可能在并行计划中使用。上面提到的两个SQLCLR函数都为true。


4

您可能要考虑使用TVF(表值函数)从数据的开头和结尾删除有问题的字符。

创建一个表来保存测试数据:

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

创建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

运行TVF以显示结果:

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

结果:

在此处输入图片说明

TVF递归调用自身,直到传递给函数的字符串的开头和结尾处没有剩余的有害字符为止。这不可能在大量行上表现良好,但是如果您正在使用它来修复将数据插入数据库中的数据,则可能行得通。

您可以在更新语句中使用它:

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


SELECT *
FROM dbo.TrimTest;

结果(以文本形式):

在此处输入图片说明


谢谢Max,不幸的是,我必须清理多个表中的大量行(百万),我希望某些函数可以在UPDATE查询中使用,例如LTRIM/ RTRIM,其中的某行UPDATE table t SET t.column = TRIM(t.column, CONCAT(CHAR(9), CHAR(10), CHAR(13)))包含TRIM( expression, charlist )接受要修剪的字符列表的函数就像许多脚本语言一样。
乔瓦尼·洛瓦托

我对它发出的警告“可能”在许多行上都无法正常运行,这可能是问题,也可能不是问题。如果只执行一次,则可能不是问题。您可能需要在非生产环境中进行测试,以便了解需要花费多长时间。
Max Vernon

我将更新答案,以显示您将如何在update声明中使用它。
Max Vernon

1

我只遇到这种特殊情况的问题,我需要查找并清理每个带有空格的字段,但是我在数据库字段中发现了4种可能的空格(参考ASCII代码表):

  • 水平制表符(char(9))
  • 换行(char(10))
  • 垂直制表符(char(9))
  • 空格(char(32))

也许此查询可以为您提供帮助。

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

这也从字段中间清除空格,而不仅仅是问题中要求的开始和结束。
科林·哈特

是的,您是对的,我将编辑
sami.almasagedi

-1

您将不得不分析第二个示例,因为LTRIM / RTRIM仅修剪空格。您实际上想修剪SQL考虑的数据(/ r,/ t等)。如果知道要查找的值,请使用REPLACE替换它们。更好的是,编写一个函数并调用它。


-1

如果愿意,请使用我的优雅功能:

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
标量值函数几乎不优雅。它们强制查询以串行方式运行,并且每行执行一次(而不是每个查询一次)。您应该查看内联表值函数。
Erik Darling

-2

对大数据使用功能可能会花费较长的执行时间。我有一个800万行的数据集,使用函数花了30多分钟来执行。replace(replace(replace(replace(@COLUMN,CHAR(9),''),CHAR(10),''),CHAR(13),''),CHAR(32),'')只花了5秒。谢谢大家 我看到你@ sami.almasagedi和@Colin't Hart


如您在重复的答案中所述,如果必须保留第一个和最后一个非空白字符之间的空白,则不能解决问题。速度只有在产生所需答案时才有用。另外-请参阅已接受答案中的注释,以了解如何确保函数不会降低这样的查询速度。
RDFozz
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.