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
字符仍然存在,它们确实存在。
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 的测试中报告的位置相同,但现在它显示了它在每种情况下匹配的代码点。是的,它等同于32
和0
。
现在,取消对该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
二进制排序规则。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)
归档时间: |
|
查看次数: |
5808 次 |
最近记录: |