web-dev-qa-db-ja.com

指定された範囲のASCII文字を含む行を検索するクエリ

別のトピックのスクリプトをいくつか使用していますが、受け入れられた回答がすべてのデータシナリオで機能しません。元の 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   |
+-------------------+
_

クエリ2: その他の回答John によると、char(150)を含む「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のいずれかにあります。私はこれを制御できないので、両方で機能する自己完結型クエリを見つけたいと思っています。クエリの目的は、一部のソフトウェアアプリケーションで正しく処理されないソーステーブル/列の文字を見つけることです。アプリケーションはソースを正しく解釈していますが、範囲はアプリケーションによって異なりますが、「標準」範囲外の文字に関する問題が発生する場合があります。

6
Fred

受け入れられた回答が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関数は、127yの間の値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構文を少し調整する必要がある場合の違い。


与えられた:

  1. クエリの目的は、一部のソフトウェアアプリケーションで正しく処理されないソーステーブル/列の文字を見つけることです。

    そして:

  2. データはVARCHARまたはNVARCHARのいずれかです。

私はそれを言うでしょう:

  1. しない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
    */
    
  2. 特に/を保持するNVARCHARを処理する場合、特定の無効な範囲の文字ではなく、特定の「有効な」範囲の文字notを探す方が信頼性が高くなります。たくさん256文字以上。
  3. 単一のクエリifで逃れることができます。「有効な」範囲は常に値0から127の間です(これらの値はどちらの場合も同じであるため)。ただし、127を超える値を指定する必要がある場合は、VARCHARに対する1つのクエリとNVARCHARに対する1つのクエリが必要になります。

言われていることすべて:

  • 次のクエリは、VARCHARNVARCHARの両方について、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
    */
    

について:

アプリケーションはソースを正しく解釈していますが、範囲はアプリケーションによって異なりますが、「標準」範囲外の文字に関する問題が発生する場合があります。

  1. アプリがソースデータを正しく解釈している場合、一部の文字で「問題」が発生する可能性があることを理解できません。
  2. このようなアプリケーションサウンドによって変化する範囲は、このような単純なQ&A形式で行うことができるよりも詳細な調査が必要になる場合があります。この動作は、接続に異なるドライバー(ODBC/OLEDB /など)を使用していること、それらが記述されている言語、取得しているデータについて想定していることが原因である可能性があります。一部の問題は、アプリの構成(コードを変更しない)で修正できる場合もあれば、コードを変更した場合にのみ修正できる場合もあります。
4
Solomon Rutzky