これは、同様の質問に基づいています Access SQLで複数の文字を置換する方法?
SQL Server 2005のwhere()句内のreplace()関数には19個の置換があるという制限があるため、これを書きました。
私には次のタスクがあります:列で一致を実行する必要があり、replace()関数を使用して複数の不要な文字を削除する一致の可能性を改善する
DECLARE @es NVarChar(1) SET @es = ''
DECLARE @p0 NVarChar(1) SET @p0 = '!'
DECLARE @p1 NVarChar(1) SET @p1 = '@'
---etc...
SELECT *
FROM t1,t2
WHERE REPLACE(REPLACE(t1.stringkey,@p0, @es), @p1, @es)
= REPLACE(REPLACE(t2.stringkey,@p0, @es), @p1, @es)
---etc
そのwhere句に19を超えるREPLACE()がある場合、機能しません。したがって、私が思いついた解決策は、この例でtrimCharsというsql関数を作成することです(@ 22
CREATE FUNCTION [trimChars] (
@string varchar(max)
)
RETURNS varchar(max)
AS
BEGIN
DECLARE @es NVarChar(1) SET @es = ''
DECLARE @p22 NVarChar(1) SET @p22 = '^'
DECLARE @p23 NVarChar(1) SET @p23 = '&'
DECLARE @p24 NVarChar(1) SET @p24 = '*'
DECLARE @p25 NVarChar(1) SET @p25 = '('
DECLARE @p26 NVarChar(1) SET @p26 = '_'
DECLARE @p27 NVarChar(1) SET @p27 = ')'
DECLARE @p28 NVarChar(1) SET @p28 = '`'
DECLARE @p29 NVarChar(1) SET @p29 = '~'
DECLARE @p30 NVarChar(1) SET @p30 = '{'
DECLARE @p31 NVarChar(1) SET @p31 = '}'
DECLARE @p32 NVarChar(1) SET @p32 = ' '
DECLARE @p33 NVarChar(1) SET @p33 = '['
DECLARE @p34 NVarChar(1) SET @p34 = '?'
DECLARE @p35 NVarChar(1) SET @p35 = ']'
DECLARE @p36 NVarChar(1) SET @p36 = '\'
DECLARE @p37 NVarChar(1) SET @p37 = '|'
DECLARE @p38 NVarChar(1) SET @p38 = '<'
DECLARE @p39 NVarChar(1) SET @p39 = '>'
DECLARE @p40 NVarChar(1) SET @p40 = '@'
DECLARE @p41 NVarChar(1) SET @p41 = '-'
return REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
@string, @p22, @es), @p23, @es), @p24, @es), @p25, @es), @p26, @es), @p27, @es), @p28, @es), @p29, @es), @p30, @es), @p31, @es), @p32, @es), @p33, @es), @p34, @es), @p35, @es), @p36, @es), @p37, @es), @p38, @es), @p39, @es), @p40, @es), @p41, @es)
END
これは、他の置換文字列に加えて使用できます
SELECT *
FROM t1,t2
WHERE trimChars(REPLACE(REPLACE(t1.stringkey,@p0, @es), @p1, @es)
= REPLACE(REPLACE(t2.stringkey,@p0, @es), @p1, @es))
同様の置換を行うために、いくつかの関数を作成しましたtrimChars(trimMoreChars(
SELECT *
FROM t1,t2
WHERE trimChars(trimMoreChars(REPLACE(REPLACE(t1.stringkey,@p0, @es), @p1, @es)
= REPLACE(REPLACE(t2.stringkey,@p0, @es), @p1, @es)))
パフォーマンスとよりクリーンな実装の点で、誰かがこの問題のより良い解決策を教えてもらえますか?
CLR UDFの作成代わりに を真剣に検討し、正規表現(文字列とパターンの両方をパラメーターとして渡すことができます)を使用して、文字の範囲。このSQL UDFを簡単に上回るはずです。
SQLの便利なトリックの1つは、@var = function(...)
を使用して値を割り当てる機能です。レコードセットに複数のレコードがある場合、varには複数の副作用が割り当てられます。
declare @badStrings table (item varchar(50))
INSERT INTO @badStrings(item)
SELECT '>' UNION ALL
SELECT '<' UNION ALL
SELECT '(' UNION ALL
SELECT ')' UNION ALL
SELECT '!' UNION ALL
SELECT '?' UNION ALL
SELECT '@'
declare @testString varchar(100), @newString varchar(100)
set @teststring = 'Juliet ro><0zs my s0x()rz!!?!one!@!@!@!'
set @newString = @testString
SELECT @newString = Replace(@newString, item, '') FROM @badStrings
select @newString -- returns 'Juliet ro0zs my s0xrzone'
@Juliettのソリューションが本当に好きです! CTEを使用して、無効な文字をすべて取得します。
DECLARE @badStrings VARCHAR(100)
DECLARE @teststring VARCHAR(100)
SET @badStrings = '><()!?@'
SET @teststring = 'Juliet ro><0zs my s0x()rz!!?!one!@!@!@!'
;WITH CTE AS
(
SELECT SUBSTRING(@badStrings, 1, 1) AS [String], 1 AS [Start], 1 AS [Counter]
UNION ALL
SELECT SUBSTRING(@badStrings, [Start] + 1, 1) AS [String], [Start] + 1, [Counter] + 1
FROM CTE
WHERE [Counter] < LEN(@badStrings)
)
SELECT @teststring = REPLACE(@teststring, CTE.[String], '') FROM CTE
SELECT @teststring
ジュリエットro0zs私のs0xrzone
スカラーのユーザー定義関数を作成することをお勧めします。これは一例です(変数名がスペイン語であるため、前もってごめんなさい):
CREATE FUNCTION [dbo].[Udf_ReplaceChars] (
@cadena VARCHAR(500), -- String to manipulate
@caracteresElim VARCHAR(100), -- String of characters to be replaced
@caracteresReem VARCHAR(100) -- String of characters for replacement
)
RETURNS VARCHAR(500)
AS
BEGIN
DECLARE @cadenaFinal VARCHAR(500), @longCad INT, @pos INT, @caracter CHAR(1), @posCarER INT;
SELECT
@cadenaFinal = '',
@longCad = LEN(@cadena),
@pos = 1;
IF LEN(@caracteresElim)<>LEN(@caracteresReem)
BEGIN
RETURN NULL;
END
WHILE @pos <= @longCad
BEGIN
SELECT
@caracter = SUBSTRING(@cadena,@pos,1),
@pos = @pos + 1,
@posCarER = CHARINDEX(@caracter,@caracteresElim);
IF @posCarER <= 0
BEGIN
SET @cadenaFinal = @cadenaFinal + @caracter;
END
ELSE
BEGIN
SET @cadenaFinal = @cadenaFinal + SUBSTRING(@caracteresReem,@posCarER,1)
END
END
RETURN @cadenaFinal;
END
この関数を使用した例を次に示します。
SELECT dbo.Udf_ReplaceChars('This is a test.','sat','Z47');
結果は:7hiZ iZ 4 7eZ7。
ご覧のとおり、@caracteresElim
パラメーターの各文字は、@caracteresReem
パラメーターと同じ位置にある文字に置き換えられます。
ソースデータが異常な/技術的な文字に加えて、CSVのユビキタスな余分なコンマを正しく出力できないという1回限りのデータ移行の問題がありました。
このような各文字について、ソース抽出はそれらを、ソースシステムとそれらをロードしているがデータには含まれないSQL Serverの両方に認識可能なものに置き換える必要があると判断しました。
ただし、さまざまなテーブルのさまざまな列にこれらの置換文字が表示され、それらを置換する必要があることを意味していました。複数のREPLACE関数をネストすると、インポートコードが怖くなり、配置と括弧の数を誤判断するエラーが発生しやすくなるため、次の関数を作成しました。 3,000行のテーブルの列を1秒未満で処理できることは知っていますが、数百万行のテーブルにどれだけ迅速に拡張できるかはわかりません。
create function [dbo].[udf_ReplaceMultipleChars]
(
@OriginalString nvarchar(4000)
, @ReplaceTheseChars nvarchar(100)
, @LengthOfReplacement int = 1
)
returns nvarchar(4000)
begin
declare @RevisedString nvarchar(4000) = N'';
declare @lengthofinput int =
(
select len(@OriginalString)
);
with AllNumbers
as (select 1 as Number
union all
select Number + 1
from AllNumbers
where Number < @lengthofinput)
select @RevisedString += case
when (charindex(substring(@OriginalString, Number, 1), @ReplaceTheseChars, 1) - 1) % 2
= 0 then
substring(
@ReplaceTheseChars
, charindex(
substring(@OriginalString, Number, 1)
, @ReplaceTheseChars
, 1
) + 1
, @LengthOfReplacement
)
else
substring(@OriginalString, Number, 1)
end
from AllNumbers
option (maxrecursion 4000);
return (@RevisedString);
end;
評価する文字列と置換する文字(@OriginalString)の両方を送信することで機能します。最初の文字は2番目に、3番目は4番目に、5番目には6番目に、というようにペアの文字列に置き換えます。 on(@ReplaceTheseChars)。
置換する必要のある文字列とその置換... ['] "〜、{Ø}°$±| ¼¦¼ª½¬½ ^¾#✓
すなわち、開き角括弧はアポストロフィを示し、閉じ角括弧は二重引用符を示します。下品な部分と度および直径の記号がそこにあったことがわかります。
長い文字列を置換する必要がある場合の開始点として含まれるデフォルトの@LengthOfReplacementがあります。私はプロジェクトでそれをいじりましたが、単一の文字の置換が主な機能でした。
Caseステートメントの条件は重要です。 @ReplaceTheseChars変数で見つかった場合にのみ文字を置き換え、奇数位置で文字を検出する必要があることを確認します(charindexの結果からマイナス1を指定すると、見つからないものはすべて負のモジュロ値を返します)。つまり、位置5にチルダ(〜)が見つかった場合、カンマに置き換えられますが、その後の実行で位置6にカンマが見つかった場合、中括弧({)に置き換えられません。
これは、例で最もよく実証できます...
declare @ProductDescription nvarchar(20) = N'abc~def[¦][123';
select @ProductDescription
= dbo.udf_ReplaceMultipleChars(
@ProductDescription
/* NB the doubling up of the apostrophe is necessary in the string but resolves to a single apostrophe when passed to the function */
,'['']"~,{Ø}°$±|¼¦¼ª½¬½^¾#✓'
, default
);
select @ProductDescription
, dbo.udf_ReplaceMultipleChars(
@ProductDescription
,'['']"~,{Ø}°$±|¼¦¼ª½¬½^¾#✓'
/* if you didn't know how to type those peculiar chars in then you can build a string like this... '[' + nchar(0x0027) + ']"~,{' + nchar(0x00D8) + '}' + nchar(0x00B0) etc */
,
default
);
これにより、関数を最初に通過した後の値と、次の2回目の値の両方が返されます。abc、def'¼ "'123 abc、def'¼"' 123
テーブルの更新はただ
update a
set a.Col1 = udf.ReplaceMultipleChars(a.Col1,'~,]"',1)
from TestTable a
最後に(私はあなたの言うことを聞きます!)、私は翻訳関数にアクセスできませんでしたが、この関数はドキュメントに示されている例を非常に簡単に処理できると信じています。 TRANSLATE関数のデモは
SELECT TRANSLATE('2*[3+4]/{7-2}', '[]{}', '()()');
これは2 *(3 + 4)/(7-2)を返しますが、2 * [3 + 4]/[7-2]では機能しない可能性があることを理解しています。
私の関数は、次のようにこれにアプローチします。置換される各文字をリストし、その後に置換[->(、{->(など。
select dbo.udf_ReplaceMultipleChars('2*[3+4]/{7-2}', '[({(])})', 1);
これも動作します
select dbo.udf_ReplaceMultipleChars('2*[3+4]/[7-2]', '[({(])})', 1);
誰かがこれが便利だと思うことを望みます。もしあなたがより大きなテーブルに対してそのパフォーマンスをテストするようになったら、私たちに何らかの形で知らせてください!
declare @testVal varchar(20)
set @testVal = '?t/es?ti/n*g 1*2?3*'
select @testVal = REPLACE(@testVal, item, '') from (select '?' item union select '*' union select '/') list
select @testVal;
この質問はSQL Server 2005について尋ねられましたが、SQL Server 2017の時点では、新しいTRANSLATE関数を使用して要求を実行できることに注意する価値があります。
https://docs.Microsoft.com/en-us/sql/t-sql/functions/translate-transact-sql
この情報が将来このページにアクセスする人々に役立つことを願っています。
1つのオプションは、数値/集計テーブルを使用して、疑似セットベースのクエリを介して反復プロセスを実行することです。
文字置換の一般的な考え方は、単純な文字マップテーブルアプローチで実証できます。
create table charMap (srcChar char(1), replaceChar char(1))
insert charMap values ('a', 'z')
insert charMap values ('b', 'y')
create table testChar(srcChar char(1))
insert testChar values ('1')
insert testChar values ('a')
insert testChar values ('2')
insert testChar values ('b')
select
coalesce(charMap.replaceChar, testChar.srcChar) as charData
from testChar left join charMap on testChar.srcChar = charMap.srcChar
次に、集計テーブルアプローチを使用して、文字列内の各文字位置を検索できます。
create table tally (i int)
declare @i int
set @i = 1
while @i <= 256 begin
insert tally values (@i)
set @i = @i + 1
end
create table testData (testString char(10))
insert testData values ('123a456')
insert testData values ('123ab456')
insert testData values ('123b456')
select
i,
SUBSTRING(testString, i, 1) as srcChar,
coalesce(charMap.replaceChar, SUBSTRING(testString, i, 1)) as charData
from testData cross join tally
left join charMap on SUBSTRING(testString, i, 1) = charMap.srcChar
where i <= LEN(testString)
Charles Bretanaが答えを削除した理由がわからないので、CWの回答として追加し直していますが、永続化された計算列は、ほとんどの場合、データをクレンジングまたは変換する必要がある場合に対処するための本当に良い方法です、元のゴミを保存する必要があります。彼の提案は、データをクレンジングする方法に関係なく、適切かつ適切です。
具体的には、現在のプロジェクトでは、先行ゼロと矛盾して格納された特定の数値識別子からすべての先行ゼロを削除する永続的な計算列があります(幸いなことに、これは直線T-SQLで実際に簡単に処理されます)。これは、テーブルで永続化された計算列に格納され、インデックスにインデックスが付けられます。これは、その適合識別子が結合でよく使用されるためです。
手順は次のとおりです
次のコードを参照してください。
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString Replace2(SqlString inputtext, SqlString filter,SqlString replacewith)
{
string str = inputtext.ToString();
try
{
string pattern = (string)filter;
string replacement = (string)replacewith;
Regex rgx = new Regex(pattern);
string result = rgx.Replace(str, replacement);
return (SqlString)result;
}
catch (Exception s)
{
return (SqlString)s.Message;
}
}
}
CLR関数を展開する
今すぐテスト
次のコードを参照してください。
create table dbo.test(dummydata varchar(255))
Go
INSERT INTO dbo.test values('P@ssw1rd'),('This 12is @test')
Go
Update dbo.test
set dummydata=dbo.Replace2(dummydata,'[0-9@]','')
select * from dbo.test
dummydata, Psswrd, This is test booom!!!!!!!!!!!!!