別のトピックのスクリプトをいくつか使用していますが、受け入れられた回答がすべてのデータシナリオで機能しません。元の Ascii以外の文字を確認する方法 の投稿について質問したのですが、まだコメントや賛成投票を行う十分な評判がありません。
質問:
私のテスト
問題を示すために、サンプルデータ、回答の1つからのストアドプロシージャ、およびクエリを使用して SQL Fiddle を作成しました。
クエリ1:sample_table
_-- Note: The "bad dash" row has char(150)
SELECT * FROM sample_table;
+-------------------+
| DataColumn |
+-------------------+
| test - good dash |
| test – bad dash |
+-------------------+
_
_SELECT dbo.Find_Invalid_Chars(DataColumn) [Invalid Characters]
FROM sample_table
WHERE dbo.Find_Invalid_Chars(DataColumn) IS NOT NULL;
+----------------------+
| Invalid Characters |
+----------------------+
| test [150] bad dash |
+----------------------+
_
クエリ3: 受け入れられた回答Martin Smith が返す結果なし:
_SELECT DataColumn AS [Bad Data]
FROM sample_table
WHERE DataColumn LIKE '%[' + CHAR(127)+ '-' +CHAR(255)+']%' COLLATE Latin1_General_100_BIN2;
+------------+
| [Bad Data] |
+------------+
-- No rows returned.
_
結論
残念ながら、ストアドプロシージャを作成できないデータベースの範囲内(または範囲外)の文字を検索する必要があることがよくあります。 受け入れられた回答 または簡単な解決策を見つけたいですオブジェクト(一時テーブルを含む)の作成を必要としないスクリプト。
助言がありますか?前もって感謝します。
EDIT 1:ソリューションは、データベース内のオブジェクトまたは設定を変更または追加できません。指定されたASCII番号または拡張ASCII番号に関係なく、2つのCHAR()
番号の範囲内の1つ以上の文字を含む行を選択する自己完結型クエリを探しています。
EDIT 2:DataColumnはVARCHAR
またはNVARCHAR
のいずれかにあります。私はこれを制御できないので、両方で機能する自己完結型クエリを見つけたいと思っています。クエリの目的は、一部のソフトウェアアプリケーションで正しく処理されないソーステーブル/列の文字を見つけることです。アプリケーションはソースを正しく解釈していますが、範囲はアプリケーションによって異なりますが、「標準」範囲外の文字に関する問題が発生する場合があります。
受け入れられた回答がchar(150)で機能しないのはなぜですか?
実際にはそうです。問題はあなたのテストが悪い/無効であるということです。テストする列DataColumn
は、NVARCHAR
ではなくVARCHAR
を使用しています。文字自体は両方のデータ型で機能しますが、それぞれの場合での使用方法により、動作は異なります。
Find_Invalid_Chars()
関数(つまり、「その他」の回答)では、文字列はその関数の入力パラメーターのデータ型であるため、VARCHAR
に変換されます。この場合、それは期待どおりに機能します(ただし、そのループよりもはるかに効率的に実行できると思いますが、それは別の時間です;-)LIKE
クエリ(つまり、「受け入れられた」回答)では、'%[' + CHAR(127)+ '-' +CHAR(255)+']%'
の拡張され連結された結果は実際にはNVARCHAR
に変換されます。これは、比較対象の列のデータ型であるためです(NVARCHAR
はより高いデータ型を持っています)優先)、したがって、そのLIKE
関数はnot期待どおりに動作します:CHAR(255)
文字が別のコードポイントにマップされるか、CHAR(150)
文字は別のコードポイントにマップされます(CHAR(127)
文字は標準のASCII範囲内にあるため変更されません)。どちらの場合も、変換NVARCHAR
に変換すると、「En Dash」文字(「–」)の数値がその範囲内になくなります。つまり、LIKE
関数は、127
とy
の間の値x
を探します(ここでx
> = 128)、「En Dash」文字のy
は> x
になりました。VARCHAR
では、x
= 255およびy
= 150になります。これが機能することを確認するための簡単な修正は、NVARCHAR
列のDataColumn
データ型をVARCHAR
に変更し(はい、最初の "N"を削除するだけです)、スキーマを再構築して実行すると、LIKE
クエリによって期待どおりに動作します。
以下は、テスト列をNVARCHAR
にすると、LIKE
クエリが行と一致しなくなった理由を説明するのに役立ちます。
SELECT UNICODE(CHAR(127)) AS [CHAR(127)],
UNICODE(CHAR(150)) AS [CHAR(150)],
UNICODE(CHAR(255)) AS [CHAR(255)];
/*
CHAR(127) CHAR(150) CHAR(255)
127 8211 255
*/
クエリの下の結果からわかるように、CHAR(150)
であった「不良ダッシュ」は、NVARCHAR
列に格納されたときにNCHAR(8211)
になりました。そして、その述語はバイナリ照合を使用しているため(通常、このシナリオでは正しいことです)、文字ではなくコードポイント/値を調べていました。したがって、LIKE
句は127から255までの値を持つ文字を探していましたが、8211は通常その範囲にありません;-)。
PS関数CHAR(150)
canは、デフォルトの照合に基づいて、異なる文字、またはNULL
を返すことに注意してくださいその関数を実行するデータベース。これは、VARCHAR
データがコードページに基づいており、それらが照合によって決定され、CHAR()
関数を実行するときに使用される照合がアクティブな/現在のデータベースの既定の照合であるためです。これは、128〜255の値に影響します。0〜127の値は、照合に関係なく、常に同じ文字を返します。これは、標準のASCII文字セットであり、サポートされるすべてのコードページで同じであるためです。 SQL Server(一般的にすべてのコードページではありません)。
PPSまた、関数とクエリのロジックのわずかな違いに気づきました(つまり、リンクされた質問からの2つの回答)。CHAR(127)
は、Find_Invalid_Chars()
関数ですが、LIKE
クエリでは無効/無効と見なされます。それが私なら、CHAR(127)
は標準のASCII文字セットの一部であるため、有効であると考えます。しかし、あなたはそれを何にするかを決める必要があります。 LIKE
構文を少し調整する必要がある場合の違い。
与えられた:
クエリの目的は、一部のソフトウェアアプリケーションで正しく処理されないソーステーブル/列の文字を見つけることです。
そして:
データはVARCHARまたはNVARCHARのいずれかです。
私はそれを言うでしょう:
しないNVARCHAR
ソースデータをVARCHAR
に変換したいのは、無効なソース文字を有効な文字に変換する「最適な」マッピングがあるかもしれないが、ソフトウェアアプリケーションは、「最適な」マッピングを使用しない場合があります。
SELECT NCHAR(178) AS [Unicode], -- Superscript 2 (U+00B2)
CONVERT(VARCHAR(5), NCHAR(178)
COLLATE SQL_Latin1_General_CP1_CI_AS) AS [CodePage-1252],
CONVERT(VARCHAR(5), NCHAR(178)
COLLATE Turkmen_100_CI_AS) AS [CodePage-1250]
/*
Unicode CodePage-1252 CodePage-1250
² ² 2
*/
NVARCHAR
を処理する場合、特定の無効な範囲の文字ではなく、特定の「有効な」範囲の文字notを探す方が信頼性が高くなります。たくさん256文字以上。VARCHAR
に対する1つのクエリとNVARCHAR
に対する1つのクエリが必要になります。言われていることすべて:
次のクエリは、VARCHAR
とNVARCHAR
の両方について、notの範囲にある0〜127の文字を少なくとも1つ含む行を返します。ただし、127を超える値のNVARCHAR
列でのみ機能します。
SELECT *
FROM (VALUES (NCHAR(178)), (NCHAR(8211)), (N''), (NULL), (N'xy' + NCHAR(165)),
(N'AA'), (N'mM' + NCHAR(999) + N'Nn'), (N'#!~()')) tmp(TestValue)
WHERE tmp.[TestValue] LIKE N'%[^' + NCHAR(0) + N'-' + NCHAR(127)
+ N']%' COLLATE Latin1_General_100_BIN2;
/*
TestValue
²
–
xy¥
mMϧNn
*/
次のクエリも、notの範囲にある0〜127の文字を少なくとも1つ含む行を返しますが、VARCHAR
列に対してのみ機能します。ただし、128〜255の値を使用できます。
SELECT *
FROM (VALUES (CHAR(178)), (CHAR(150)), (''), (NULL), ('AA'), ('#!~()'),
('xy' + CONVERT(VARCHAR(5), NCHAR(165) COLLATE Latin1_General_100_BIN2)),
('mM' + CONVERT(VARCHAR(5), NCHAR(199) COLLATE Latin1_General_100_BIN2) + 'Nn')
) tmp(TestValue)
WHERE tmp.[TestValue] LIKE '%[^' + CHAR(0) + '-' + CHAR(127)
+ ']%' COLLATE Latin1_General_100_BIN2;
/*
TestValue
²
–
xy¥
mMÇNn
*/
について:
アプリケーションはソースを正しく解釈していますが、範囲はアプリケーションによって異なりますが、「標準」範囲外の文字に関する問題が発生する場合があります。