web-dev-qa-db-ja.com

列の特殊文字をスペースで置き換える

特殊文字をスペースに置き換えるクエリを作成しようとしています。以下のコードは、行を識別するのに役立ちます。 (英数字、カンマ、スペースは有効です):

SELECT columnA
FROM tableA
WHERE columnA like '%[^a-Z0-9, ]%'

置換関数をselectステートメントに統合して、結果セット内の英数字、コンマ、スペース以外のすべての文字を ''(スペース)で置き換えるにはどうすればよいですか。これは動作しません:

SELECT replace(columnA,'%[^a-Z0-9, ]%',' ')
FROM tableA
WHERE columnA like '%[^a-Z0-9, ]%'
10

アメリカ英語のアルファベット(大文字と小文字の両方のバージョン)の26文字のみを使用することが保証されている場合は、LIKEおよび/またはPATINDEXを使用して、[a-z]の単純な範囲表記を使用して回避できます。大文字と小文字を区別しない照合を使用する場合は、大文字の「Z」を使用する必要はありません)。

ただし、en-USアルファベットにはない文字が、VARCHARデータのさまざまなコードページ/照合で使用できる場合(Þ =ラテン大文字 "Thorn" = SELECT CHAR(0xDE)など)、次の操作が必要になる場合があります。これらを文字クラスに含めます:[a-z0-9, Þ]。もちろん、それらの余分な文字はコードページごとです。

また、照合タイプ(SQL ServerとWindows)と感度設定(大文字と小文字、アクセントなど、大文字と小文字を区別)は、特定の範囲に含まれる文字に影響することに注意してください。たとえば、SQL Server照合順序は大文字と小文字をWindows照合順序とは逆の順序で並べ替えます。つまり、両方のタイプの照合で大文字と小文字が区別される照合を想定すると、一方はAaBb...を実行し、もう一方はaAbB...を実行します。その結果、aは、一方のA-Zの範囲内にありますが、もう一方の範囲にはありません。また、Aの値が65でaが97であることを考えると、a-Zの範囲は、バイナリ照合順序(_BINまたは_BIN2で終わるが、_BINは使用しない)のどの文字とも一致しません。したがって、範囲は無効です。 97から65の;-)。バリエーションが多すぎてここでの例を示すことができないので、近いうちにブログに詳細な説明を投稿するつもりです(その後、リンクを更新してこれを更新します)。ただし、他の言語から有効な文字を取得する場合でも、米国英語の文字のみを受け入れることに厳格になる場合は、おそらく次のパターン照合:

LIKE '%[^A-Za-z0-9, ]%' COLLATE Latin1_General_100_BIN2

ここで、NVARCHARデータをサポートしていて、さまざまな言語から "Word"文字を取得できる場合、T-SQLはこれらを区別するための実際の方法がないため、あまり役に立ちません。この場合、正規表現(RegEx)、具体的にはReplaceメソッド/関数を使用する必要があります。これらはSQLCLRを通じてのみ使用できます。以下は、いくつかの「特殊」文字を置き換えるが、少なくとも1つの言語で有効な文字をすべて残す例を示しています。

DECLARE @Test NVARCHAR(500);
SET @Test = N'this$is%a<>TEST,;to}⌡↕strip╞╟╚══¶out_ç_ƒ▀ special-ij-೫-chars-舛-დ-א-B';
SELECT SQL#.RegEx_Replace4k(@Test, N'[\W\p{Pc}-[,]]', N' ', -1, 1, NULL); 

戻り値:

this is a  TEST, to   strip      out ç ƒ  special ij ೫ chars 舛 დ א B

RegEx式の意味は次のとおりです。

  • \W =「任意のnon-Word文字」を意味するRegEx "エスケープ"
  • \p{Pc} =「句読点、コネクター」のUnicode「カテゴリー」(これは、「\Wエスケープによってこの「カテゴリー」が明確に除外されているためにのみ、一致に必要です)
  • -[,] =クラス減算(これは、\Wエスケープに含まれているため、「特殊」としてコンマをマッチングから除外するために必要です)

次のコマンドを発行するだけで、テーブルを更新できます。

UPDATE tbl
SET    tbl.field = SQL#.RegEx_Replace4k(tbl.field, N'[\W\p{Pc}-[,]]', N' ', -1, 1, NULL)
FROM   tbl
WHERE  SQL#.RegEx_IsMatch4k(tbl.field, N'[\W\p{Pc}-[,]]', 1, NULL) = 1;

これらの例では、私が作成したSQLCLR関数の無料バージョン SQL# ライブラリで使用可能な2つの関数を使用したことに注意してください(ただし、これらも無料です)。また、NVARCHAR(4000)パラメータタイプではなくNVARCHAR(MAX)を使用しているため、より高速な「4k」バージョンを使用したことにも注意してください。データがNVARCHAR(MAX)を使用している場合は、関数名から「4k」を削除するだけです。

こちらもご覧ください:

11
Solomon Rutzky

私は投稿があります ここでは似たようなことをしています

基本的に、私は再帰CTEを使用してループを何度も繰り返し、一度に1つの「不良」文字を置き換えています。 STUFFを使用して1文字(スペースで置き換えることができます)を削除し、PATINDEXを使用して削除する文字の場所を見つけています。あなたが探していることをするために少し修正することができます。ただし、「良い」リストが作成され、実際には既存のリストは更新されません。

DECLARE @Pattern varchar(50) = '%[^A-Za-z0-9, ]%';

WITH FixBadChars AS (SELECT StringToFix, StringToFix AS FixedString, 1 AS MyCounter, Id
                FROM BadStringList
                UNION ALL
                SELECT StringToFix, Stuff(FixedString, PatIndex(@Pattern, 
                    FixedString COLLATE Latin1_General_BIN2), 1, ' ') AS FixedString, 
                    MyCounter + 1, Id
                FROM FixBadChars
                WHERE FixedString COLLATE Latin1_General_BIN2 LIKE @Pattern)
SELECT StringToFix, FixedString, MyCounter, Id
FROM FixBadChars
WHERE MyCounter = 
        (SELECT MAX(MyCounter) 
        FROM FixBadChars Fixed
        WHERE Fixed.Id = FixBadChars.Id)
OPTION (MAXRECURSION 1000);

クエリだけでなく更新を行うために下部を変更できるはずですが、実際には試していません。私はそれがこのようなものになるだろうとかなり確信しています:

UPDATE FixBadChars
SET StringToFix = FixedString
WHERE MyCounter = 
        (SELECT MAX(MyCounter) 
        FROM FixBadChars Fixed
        WHERE Fixed.Id = FixBadChars.Id)
OPTION (MAXRECURSION 1000);

スケーラビリティに関しては、30秒未満で約17万のクリーンな行を返しました。もう一度、更新を行うかどうかはわかりませんが、これは私のラップトップでした。これは、6 GBのRAMでかなり遅いです。

5
Kenneth Fisher
Declare @String nchar(2000)='hg$%^AB,.:23ab-=+'

Declare @NewString VARCHAR(2000)=''
Declare @Lenght int=LEN(@String)
Declare @Index int=1

WHILE (@Index <= @Lenght)
BEGIN
    Declare @Letter nchar(1)=Substring(@String,@Index,1);
    Declare @ASCII int=ASCII(@Letter);
    If((@ASCII >= 48 and @ASCII <= 57) or (@ASCII >= 97 and @ASCII <= 122) or (@ASCII >= 65 and @ASCII <= 90))
    BEGIN
        SET @NewString += @Letter
    END
    ELSE
    BEGIN
        SET @NewString += ' '
    END
    SET @Index+=1

END
Select @NewString
0
William Mendoza