无法从 NVARCHAR 列中删除字符 0x0000

tto*_*sen 7 sql-server collation sql-server-2008-r2 unicode

所以,我知道所有关于 Replace 函数和 char(0) 的错误。

我有一列 ( NVARCHAR(128)) 有一些NCHAR(0x0000)来自错误导入的字符。

我正在使用 SQL Server 2008 R2。

该列的排序规则是:SQL_Latin1_General_CP1_CI_AS

我已经尝试了所有可能在网上找到的东西,但没有任何东西可以从列中取出臭气熏天的 char(0) 字符。

这是我的最新尝试,结果是 BAFFLING(sql server 中的错误?)。

我有一个循环遍历每个字符并用特定字符替换 0x0000 的函数。

ALTER FUNCTION dbo.ReplaceCharZero
(
  @testString NVARCHAR(MAX),
  @charToReplaceWith NCHAR(1) = ' '
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
    DECLARE
        @i INT = 1 ,
        @fixedString NVARCHAR(MAX) = ''
    WHILE @i <= LEN(@testString)
        BEGIN
            IF SUBSTRING(@testString, @i, 1) = CHAR(0x00)
                BEGIN
                    --PRINT 'Found' + CAST(@i AS VARCHAR)
                    SET @fixedString = @fixedString + @charToReplaceWith         
                END
            ELSE
                BEGIN
                    --PRINT 'NOT Found' + CAST(@i AS VARCHAR)
                    SET @fixedString = @fixedString
                        + SUBSTRING(@testString, @i, 1)
                END
            SET @i = @i + 1
        END
    RETURN @fixedString               
END
Run Code Online (Sandbox Code Playgroud)

这就是我要测试的内容:

BEGIN TRAN

    DECLARE @ShortDescription NVARCHAR(128), @SupplierId INT, @Language CHAR(2)

    SELECT TOP 1 @ShortDescription = ShortDescription,
                 @SupplierId = SupplierID,
                 @Language = Language
    FROM   Supplier_Multilingual
    WHERE  ShortDescription LIKE '%' +  CHAR(0x00) + '%'

    SET @ShortDescription =  REPLACE(dbo.ReplaceCharZero(@ShortDescription, ' '), '-', ' ')

    UPDATE dbo.Supplier_MultiLingual
    SET    ShortDescription = NULL 
    WHERE  SupplierID = @SupplierId
    AND    Language = @Language

    UPDATE dbo.Supplier_MultiLingual
    SET    ShortDescription = dbo.ReplaceCharZero(@ShortDescription, '') 
    WHERE  SupplierID = @SupplierId
    AND    Language = @Language

    SELECT *
    FROM   Supplier_Multilingual
    WHERE  SupplierId = @SupplierId
    AND    Language = @Language
    AND    ShortDescription LIKE '%' +  CHAR(0x00) + '%'

ROLLBACK TRAN
Run Code Online (Sandbox Code Playgroud)

在我的测试中,我将该列作为变量,在其上运行我的函数以去除0x0000,然后用 更新原始列NULL,然后将其更新为我的固定变量,然后运行查询以查看是否0x0000字符仍然存在,它们确实存在。

Sol*_*zky 7

REPLACE功能中的错误?还是一般在 SQL Server 中?我不太确定。这里唯一的“问题”仅仅是没有完全理解如何处理字符串比较。

排序规则定义了某些字符将如何与其他字符进行比较。有时,某些字符组合等同于一个或多个其他字符。并且有关于诸如0x00(null) 和0x20(space) 等字符彼此或其他字符相等的规则。而且,为了让生活更有趣,VARCHAR使用 SQL Server 排序规则(即以 开头的排序规则)有一些特定于数据的细微差别SQL_,如以下示例所示:

SELECT REPLACE('VARCHAR with SQL_Latin1_General_CP1_CI_AS'+CHAR(0)+'Matches', CHAR(0),
               ': ' COLLATE SQL_Latin1_General_CP1_CI_AS);
SELECT REPLACE(N'NVARCHAR with SQL_Latin1_General_CP1_CI_AS'+NCHAR(0)+N'Matches', NCHAR(0),
               N': ' COLLATE SQL_Latin1_General_CP1_CI_AS);
SELECT REPLACE('VARCHAR with Latin1_General_100_CI_AS'+CHAR(0)+'Matches', CHAR(0),
               ': ' COLLATE Latin1_General_100_CI_AS);
SELECT REPLACE(N'NVARCHAR with Latin1_General_100_CI_AS'+NCHAR(0)+N'Matches', NCHAR(0),
               N': ' COLLATE Latin1_General_100_CI_AS);
Run Code Online (Sandbox Code Playgroud)

返回:

VARCHAR with SQL_Latin1_General_CP1_CI_AS: Matches

NVARCHAR with SQL_Latin1_General_CP1_CI_AS

VARCHAR with Latin1_General_100_CI_AS

NVARCHAR with Latin1_General_100_CI_AS
Run Code Online (Sandbox Code Playgroud)

因此,让我们使用基于 @Max's answer 中的查询来查看此行为。我将N前缀添加到字符串文字和CHAR(0), 并且我还添加了一个额外的内容,NCHAR(0)只是为了让下一部分更容易看到。我添加了查询以显示正在使用的实际代码点(以证明这些0x0000值确实在那里,并调用REPLACE()以查看其中是否真的有错误)。

DECLARE @Data NVARCHAR(255);
SELECT @Data = N'this is' + NCHAR(0) + N'a test' + NCHAR(0) + N'of null';

SELECT @Data;
SELECT CONVERT(VARBINARY(50), @Data);
SELECT REPLACE(@Data, NCHAR(0), N'~');
Run Code Online (Sandbox Code Playgroud)

这是

0x7400680069007300200069007300 0000 610020007400650073007400 0000 6F00660020006E0075006C006C00

这是

第一个结果显示字符串由于(null)终止而在“is”之后结束。第二个结果显示了底层代码,我强调了0x0000字符的两个实例。第三个结果表明该REPLACE函数似乎与传入的0x0000字符不匹配NCHAR(0)

但是我们应该期待NCHAR(0)在这里匹配吗?我们可以通过强制二进制排序来有效地禁用通常应用于字符串比较的所有等价规则。我们将使用_BIN2排序规则,因为_BIN排序规则已被弃用,除非您有特定需要,否则不应使用它们。

将以下查询添加到上面的集合并重新运行批处理。

SELECT REPLACE(@Data, NCHAR(0) COLLATE Latin1_General_100_BIN2, N'~');
Run Code Online (Sandbox Code Playgroud)

您应该得到以下附加结果:

这是~一个测试~null

因此,该REPLACE功能确实有效,并且已在 SQL Server 2008 R2、SP3 和 SQL Server 2012 SP2 上进行了测试。


好的,这样只解决了REPLACE不使用的问题NCHAR(0),但没有解决NCHAR(0)等同于空格(即NCHAR(32)NCHAR(0x20))的问题。

现在我们将使用@Max 回答中主要查询的改编版。我再次向NCHAR(0)测试字符串添加了一个额外字符(实际上只是用它替换了位置 8 处的空格),并将匹配字符的代码点添加到RAISERROR消息中。

SET NOCOUNT ON;
GO
DECLARE @Data NVARCHAR(255);
SELECT @Data = N'this is' + NCHAR(0) + N'a test' + NCHAR(0) + N'of null';

DECLARE @i INT,
        @CodePoint INT;
SET @i = 1;

WHILE @i < LEN(@Data)
BEGIN
    IF SUBSTRING(@Data, @i, 1) = NCHAR(0) --COLLATE Latin1_General_100_BIN2
    BEGIN
        SET @CodePoint = UNICODE(SUBSTRING(@Data, @i, 1));
        RAISERROR (N'Found a NULL char (Code Point = %d) at position: %d',
                   10, 1, @CodePoint, @i) WITH NOWAIT;
    END;
    SET @i = @i + 1;
END;
Run Code Online (Sandbox Code Playgroud)

此查询(该COLLATE子句仍被注释掉)将返回:

Found a NULL char (Code Point = 32) at position: 5
Found a NULL char (Code Point = 0) at position: 8
Found a NULL char (Code Point = 32) at position: 10
Found a NULL char (Code Point = 0) at position: 15
Found a NULL char (Code Point = 32) at position: 18
Run Code Online (Sandbox Code Playgroud)

这些与@Max 的测试中报告的位置相同,但现在它显示了它在每种情况下匹配的代码点。是的,它等同于320

现在,取消对该COLLATE子句的注释并重新运行它。它将返回:

Found a NULL char (Code Point = 0) at position: 8
Found a NULL char (Code Point = 0) at position: 15
Run Code Online (Sandbox Code Playgroud)

实现此目的的另一种方法是在不使用COLLATE子句的情况下将IF语句更改为:

IF ( UNICODE(SUBSTRING(@Data, @i, 1)) = 0 )
Run Code Online (Sandbox Code Playgroud)

当然,这两个修复——WHILE使用COLLATE子句或UNICODE()函数的循环——都不需要解决0x0000从输入数据中删除字符的原始问题,因为简单REPLACE(使用COLLATE子句)处理这个问题。


概括:

  • 如果您想从字符串中替换/删除字符,则无需使用循环。REPLACE只要您_BIN2通过COLLATE关键字为 3 个输入参数中的至少一个指定排序规则,该函数就可以正常工作(从技术上讲,哪种二进制排序规则无关紧要,因为二进制排序规则仅比较数字代码点值)。
  • 如果需要测试特定的代码点(例如在上面显示的循环中),那么使用该UNICODE()函数可能是最快的,因为它只报告实际存在的值。这应该比使用COLLATE关键字更快,因为它必须做更多的工作。
  • 如果需要测试一系列代码点/字符,则使用COLLATE关键字,指定_BIN2二进制排序规则。


Han*_*non 4

CHAR(0)似乎被转换为空格,或CHAR(32)

下面演示了这个问题:

DECLARE @Data NVARCHAR(255);
SELECT @Data = 'this is a test' + CHAR(0) + 'of null';
DECLARE @i INT;
SET @i = 1;
DECLARE @txt NVARCHAR(255);
WHILE @i < LEN(@Data)
BEGIN
    IF SUBSTRING(@Data, @i, 1) = CHAR(0) 
    BEGIN
        SET @txt = 'found a null char at position ' + CONVERT(NVARCHAR(255),@i);        
        RAISERROR (@txt, 0, 1) WITH NOWAIT;
    END
    SET @i = @i + 1;
END
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述