web-dev-qa-db-ja.com

SQL Replace関数内の正規表現パターン?

SELECT REPLACE('<strong>100</strong><b>.00 GB', '%^(^-?\d*\.{0,1}\d+$)%', '');

数値の2つの部分の間のマークアップを上記の正規表現に置き換えたいのですが、うまくいかないようです。 '%[^0-9]%'のような単純なものをテストするためだけに試したが、どちらも機能しなかったため、正規表現の構文が間違っているかどうかはわかりません。誰も私がこれを達成する方法を知っていますか?

63
JanT

PATINDEX を使用して、パターン(文字列)の最初のインデックスを検索できます。次に、 STUFF を使用して、一致したpattern(string)に別の文字列を挿入します。

各行をループします。不正な文字をそれぞれ希望の文字に置き換えます。あなたの場合、非数値を空白に置き換えてください。内側のループは、ループの現在のセルに複数の不正な文字がある場合です。

DECLARE @counter int

SET @counter = 0

WHILE(@counter < (SELECT MAX(ID_COLUMN) FROM Table))
BEGIN  

    WHILE 1 = 1
    BEGIN
        DECLARE @RetVal varchar(50)

        SET @RetVal =  (SELECT Column = STUFF(Column, PATINDEX('%[^0-9.]%', Column),1, '')
        FROM Table
        WHERE ID_COLUMN = @counter)

        IF(@RetVal IS NOT NULL)       
          UPDATE Table SET
          Column = @RetVal
          WHERE ID_COLUMN = @counter
        ELSE
            break
    END

    SET @counter = @counter + 1
END

注意:ただし、これは遅いです! varchar列があると影響があります。そのため、LTRIM RTRIMを使用すると少し役立つ場合があります。とにかく、遅いです。

クレジットは this StackOverFlowの答えになります。

EDITクレジットは@srutzkyにも送られます

編集(by Tmdean)一度に1行ずつ実行する代わりに、この回答をよりセットベースのソリューションに適合させることができます。それでも、1行の非数値文字の最大数を反復処理するため、理想的ではありませんが、ほとんどの状況で許容できると思います。

WHILE 1 = 1 BEGIN
    WITH q AS
        (SELECT ID_Column, PATINDEX('%[^0-9.]%', Column) AS n
        FROM Table)
    UPDATE Table
    SET Column = STUFF(Column, q.n, 1, '')
    FROM q
    WHERE Table.ID_Column = q.ID_Column AND q.n != 0;

    IF @@ROWCOUNT = 0 BREAK;
END;

フィールドがまだスクラブされているかどうかを示すビット列をテーブルに保持すると、効率を大幅に向上させることもできます。 (私の例ではNULLは「不明」を表し、列のデフォルトである必要があります。)

DECLARE @done bit = 0;
WHILE @done = 0 BEGIN
    WITH q AS
        (SELECT ID_Column, PATINDEX('%[^0-9.]%', Column) AS n
        FROM Table
        WHERE COALESCE(Scrubbed_Column, 0) = 0)
    UPDATE Table
    SET Column = STUFF(Column, q.n, 1, ''),
        Scrubbed_Column = 0
    FROM q
    WHERE Table.ID_Column = q.ID_Column AND q.n != 0;

    IF @@ROWCOUNT = 0 SET @done = 1;

    -- if Scrubbed_Column is still NULL, then the PATINDEX
    -- must have given 0
    UPDATE table
    SET Scrubbed_Column = CASE
        WHEN Scrubbed_Column IS NULL THEN 1
        ELSE NULLIF(Scrubbed_Column, 0)
    END;
END;

スキーマを変更したくない場合は、中間値をテーブル値変数に保存し、最後に実際のテーブルに適用するように簡単に調整できます。

52
Mukus

一般的な意味で、SQL Serverは正規表現をサポートしていないため、ネイティブT-SQLコードで使用できません。

それを行うCLR関数を作成できます。たとえば、 here を参照してください。

23
Szymon

見つかった文字をその単独の位置で取り除く代わりに、Replace(Column, BadFoundCharacter, '')を使用すると、かなり速くなります。さらに、各列で次に見つかった1つの不良文字を単に置き換えるのではなく、見つかったすべての文字を置き換えます。

WHILE 1 = 1 BEGIN
    UPDATE dbo.YourTable
    SET Column = Replace(Column, Substring(Column, PatIndex('%[^0-9.-]%', Column), 1), '')
    WHERE Column LIKE '%[^0-9.-]%'
    If @@RowCount = 0 BREAK;
END;

操作が少ないためだけに、これは受け入れられた答えよりもうまくいくと確信しています。他にももっと高速な方法がありますが、今はそれらを探索する時間はありません。

18
ErikE

私は他の何かを探してこの投稿に出くわしましたが、はるかに効率的であり、実際にはセットベースのクエリで使用する場合はすべての関数のデフォルト実装であるソリューションを言及すると思いましたテーブル関数。トピックはまだアクティブなようですので、うまくいけば、これは誰かに役立つでしょう。

ランダムなnewidから文字を削除する1m行のテストセットに基づく再帰セットベースのクエリまたはスカラー関数の実行に基づくこれまでのいくつかの回答のランタイムの例は、WHILEループの例では34sから2m05s、1m3sから{関数の例についてはforever}。

クロスアプライでテーブル関数を使用すると、10sで同じ目標が達成されます。最大長など、ニーズに合わせて調整する必要がある場合があります。

関数:

CREATE FUNCTION [dbo].[RemoveChars](@InputUnit VARCHAR(40))
RETURNS TABLE
AS
RETURN
    (
        WITH Numbers_prep(Number) AS
            (
                SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
            )
        ,Numbers(Number) AS
            (
                SELECT TOP (ISNULL(LEN(@InputUnit),0))
                    row_number() OVER (ORDER BY (SELECT NULL))
                FROM Numbers_prep a
                    CROSS JOIN Numbers_prep b
            )
        SELECT
            OutputUnit
        FROM
            (
                SELECT
                    substring(@InputUnit,Number,1)
                FROM  Numbers
                WHERE substring(@InputUnit,Number,1) like '%[0-9]%'
                ORDER BY Number
                FOR XML PATH('')
            ) Sub(OutputUnit)
    )

使用法:

UPDATE t
SET column = o.OutputUnit
FROM ##t t
CROSS APPLY [dbo].[RemoveChars](t.column) o
3
SQLGobbleDeGook

これは、以前の回答に基づいてこれを達成するために作成した関数です。

CREATE FUNCTION dbo.RepetitiveReplace
(
    @P_String VARCHAR(MAX),
    @P_Pattern VARCHAR(MAX),
    @P_ReplaceString VARCHAR(MAX),
    @P_ReplaceLength INT = 1
)
RETURNS VARCHAR(MAX)
BEGIN
    DECLARE @Index INT;

    -- Get starting point of pattern
    SET @Index = PATINDEX(@P_Pattern, @P_String);

    while @Index > 0
    begin
        --replace matching charactger at index
        SET @P_String = STUFF(@P_String, PATINDEX(@P_Pattern, @P_String), @P_ReplaceLength, @P_ReplaceString);
        SET @Index = PATINDEX(@P_Pattern, @P_String);
    end

    RETURN @P_String;
END;

要旨

編集:

もともと私は、SQLサーバーでうまく機能しない再帰関数を持っていました.32のネストレベルの制限があるため、関数で32回以上の置換をしようとすると、次のようなエラーが発生します。サーバーレベルを変更してネストを許可する(これは、ループが終了しないように危険な場合がある)のではなく、Whileループに切り替える方が理にかなっています。

最大のストアドプロシージャ、関数、トリガー、またはビューのネストレベルを超えています(制限32)。

3
jkdba

ソリューションをSQL関数内にラップすると、再利用したい場合に便利です。私は細胞レベルでそれをしているので、これを別の答えとして入れています:

CREATE FUNCTION [dbo].[fnReplaceInvalidChars] (@string VARCHAR(300))
RETURNS VARCHAR(300)
BEGIN
    DECLARE @str VARCHAR(300) = @string;
    DECLARE @Pattern VARCHAR (20) = '%[^a-zA-Z0-9]%';
    DECLARE @Len INT;
    SELECT @Len = LEN(@String); 
    WHILE @Len > 0 
    BEGIN
        SET @Len = @Len - 1;
        IF (PATINDEX(@Pattern,@str) > 0)
            BEGIN
                SELECT @str = STUFF(@str, PATINDEX(@Pattern,@str),1,'');    
            END
        ELSE
        BEGIN
            BREAK;
        END
    END     
    RETURN @str
END
2
Ivan Rascon

ストアドプロシージャに入るパラメータに対してのみこれを行う場合は、次を使用できます。

while PatIndex('%[^0-9]%', @Param) > 0
    select  @Param = Replace(@Param, Substring(@Param, PatIndex('%[^0-9]%', @Param), 1), '')
1
goddess_elli

私は、アルファベットの各文字を反復するのがより簡単で速いアプローチだと思います:

DECLARE @i int
SET @i = 0

WHILE(@i < 256)
BEGIN  

    IF char(@i) NOT IN ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.')      

      UPDATE Table SET Column = replace(Column, char(@i), '')

    SET @i = @i + 1

END
0
Gregorio

時間フィールドに数字以外の文字を含む文字列をクリーンアップするために、この関数を作成しました。この時間には、20:??のような議事録を追加しなかったときに疑問符が含まれていました。関数は各文字をループし、?を置き換えます0で:

 CREATE FUNCTION [dbo].[CleanTime]
(
    -- Add the parameters for the function here
    @intime nvarchar(10) 
)
RETURNS nvarchar(5)
AS
BEGIN
    -- Declare the return variable here
    DECLARE @ResultVar nvarchar(5)
    DECLARE @char char(1)
    -- Add the T-SQL statements to compute the return value here
    DECLARE @i int = 1
    WHILE @i <= LEN(@intime)
    BEGIN
    SELECT @char =  CASE WHEN substring(@intime,@i,1) like '%[0-9:]%' THEN substring(@intime,@i,1) ELSE '0' END
    SELECT @ResultVar = concat(@ResultVar,@char)   
    set @i  = @i + 1       
    END;
    -- Return the result of the function
    RETURN @ResultVar

END
0
Nordin