だから、私はReplace関数とchar(0)のバグについてすべて知っています。
不正なインポートからのNVARCHAR(128)
文字を含む列(NCHAR(0x0000)
)があります。
SQL Server 2008 R2を使用しています。
列の照合順序はSQL_Latin1_General_CP1_CI_AS
です。
私はオンラインで私が見つけることができるすべてのものを試しました、そして、何もカラムから悪臭のあるchar(0)文字を取得しません。
これが私の最新の試みであり、BAFFLING(SQLサーバーのバグ?)の結果です。
各文字をループし、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
そして、これが私がテストするために行うことです:
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
テストでは、列を変数として取得し、関数を実行して0x0000
を取り除き、元の列をNULL
で更新してから、固定列に更新します次に、クエリを実行して、0x0000
文字がまだ存在するかどうかを確認します。
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
REPLACE
関数のバグ?またはSQL Server全般では?それについてはよくわかりません。ここでの唯一の「問題」は、文字列比較がどのように処理されるかを完全に理解しているだけではありません。
照合順序は、特定の文字を他の文字と比較する方法を定義します。時には、1つ以上の他の文字と同等の文字の特定の組み合わせについてのルールがあります。また、_0x00
_(null)や_0x20
_(スペース)などの文字は、互いにまたは他の文字と同等であるというルールがあります。また、人生をより面白くするために、次の例に示すように、SQL Server照合順序(つまり、_SQL_
_で始まるもの)を使用したVARCHAR
データに固有のニュアンスがいくつかあります。
_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);
_
戻り値:
_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
_
そこで、@ Maxの answer にあるクエリに基づいたクエリを使用して、この動作を見てみましょう。文字列リテラルとCHAR(0)
にN
プレフィックスを追加しました。また、次の部分を見やすくするために、追加の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'~');
_
これは
0x7400680069007300200069007300000061002000740065007300740000006F00660020006E0075006C006C00
これは
最初の結果は、_(null)
_の終了により、文字列が「is」の直後に終了することを示しています。 2番目の結果は基になるコードを示しており、_0x0000
_文字の2つのインスタンスを強調しています。 3番目の結果は、REPLACE
関数が、渡されたNCHAR(0)
と_0x0000
_文字が一致しないように見えることを示しています。
しかし、NCHAR(0)
がここで一致することを期待する必要がありますか?バイナリ照合を強制することにより、文字列比較に通常適用されるすべての等価ルールを効果的に無効にすることができます。 __BIN2
_照合は非推奨であり、特に必要がない限り使用すべきではないため、__BIN
_照合を使用します。
上記のセットに次のクエリを追加して、バッチを再実行します。
_SELECT REPLACE(@Data, NCHAR(0) COLLATE Latin1_General_100_BIN2, N'~');
_
次の追加の結果が得られるはずです。
これはヌルのテストです
したがって、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;
_
このクエリ(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
_
これらは@Maxのテストで報告されたものと同じ位置ですが、それぞれのケースでどのコードポイントが一致しているかを示しています。そして、はい、それは_32
_と_0
_の両方と同等です。
ここで、COLLATE
句のコメントを外して、再実行します。戻ります:
_Found a NULL char (Code Point = 0) at position: 8
Found a NULL char (Code Point = 0) at position: 15
_
これを行うもう1つの方法は、COLLATE
句を使用せずに、IF
ステートメントを次のように変更することです。
_IF ( UNICODE(SUBSTRING(@Data, @i, 1)) = 0 )
_
もちろん、これらの2つの修正(WHILE
句またはUNICODE()
関数のいずれかを使用するCOLLATE
ループ)のどちらも、元の問題を解決するために必要です単純なREPLACE
(COLLATE
句を使用)がそれを処理するため、入力データの_0x0000
_文字。
まとめ:
REPLACE
関数は、COLLATE
キーワードを介して3つの入力パラメーターの少なくとも1つに__BIN2
_照合を指定する限り、正常に機能します(技術的にはどちらでもかまいません。バイナリ照合は数値コードポイント値のみを比較するため、バイナリ照合)。UNICODE()
関数を使用するのがおそらく最も高速です。これは、実際にそこにある値を報告するだけだからです。これはCOLLATE
キーワードを使用するよりも高速になるはずです。COLLATE
キーワードを使用して、__BIN2
_バイナリ照合順序を指定します。