2

大規模なデータベースのフィールドでデータ クレンジングを実行しようとしています。必要に応じて、置換された単語、マクロを含む参照テーブルがあります。これらの変更を、可能な限り最も効率的な方法で、数百万行を含むテーブルに適用したいと考えています。そうは言っても、プロセスを視覚化できるように、以下にダミーデータをいくつか提供しましょう。

Street_Addresses テーブル:

Street_Name       | Expanded_Name
------------------+--------------
100 Main St Ste 5 | NULL
25 10th Ave Apt 2 | NULL
75 Bridge Rd      | NULL

Word_Substitutions テーブル:

Word | Replacement
-----+------------
St   | Street
Ave  | Avenue
Rd   | Road
Ste  | Suite
Apt  | Apartment

したがって、更新後の最終結果は次のようになります。

Street_Name       | Expanded_Name
------------------+--------------
100 Main St Ste 5 | 100 Main Street Suite 5
25 10th Ave Apt 2 | 25 10th Avenue Apartment 2
75 Bridge Rd      | 75 Bridge Road

ここでの課題は、実行する必要がある膨大な数の置換であり、実際には単一の値に対して複数の置換が行われます。頭に浮かんだ最初の考えは、スカラー関数を使用してこのロジックをカプセル化することでした。しかし、ご想像のとおり、これは何百万行にも及ぶパフォーマンスではありません。

CREATE FUNCTION Substitute_Words (@Text varchar(MAX))
RETURNS varchar(MAX) AS
BEGIN
    SELECT @Text = REPLACE(' ' + @Text + ' ', ' ' + Word + ' ',
    ' ' + Replacement + ' ') FROM Word_Substitutions
    RETURN LTRIM(RTRIM(@Text))
END

代わりにセットベースの操作を検討することにし、次のことを思いつきました。

WHILE (1 = 1)
BEGIN
    UPDATE A SET Expanded_Name = LTRIM(RTRIM(REPLACE(
    ' ' + ISNULL(A.Expanded_Name, A.Street_Name) + ' ',
    ' ' + W.Word + ' ', ' ' + W.Replacement + ' ')))
    FROM Street_Addresses AS A
    CROSS APPLY (SELECT TOP 1 Word, Replacement
    FROM Word_Substitutions WHERE CHARINDEX(' ' + Word + ' ',
    ' ' + ISNULL(A.Expanded_Name, A.Street_Name) + ' ') > 0) AS W

    IF (@@ROWCOUNT = 0)
        BREAK
END

現在、これには実際のデータセットに基づいて約 2 時間かかります。可能であればそれを減らしたいと考えています。最適化の提案はありますか?

アップデート:

代わりに内部結合を使用するだけで、実行時間を約 5 分に短縮できました。最初は、複数の行を返す内部結合で update を使用するとうまくいかないと思っていました。更新は引き続き機能するように見えますが、ソース行は複数ではなく単一の更新を取得します。どうやら SQL Server は、更新のためにランダムな結果行を選択し、他の行を破棄します。

WHILE (1 = 1)
BEGIN
    UPDATE A SET Expanded_Name = LTRIM(RTRIM(REPLACE(
    ' ' + ISNULL(A.Expanded_Name, A.Street_Name) + ' ',
    ' ' + W.Word + ' ', ' ' + W.Replacement + ' ')))
    FROM Street_Addresses AS A
    INNER JOIN Word_Substitutions AS W ON CHARINDEX(' ' + W.Word + ' ',
    ' ' + ISNULL(A.Expanded_Name, A.Street_Name) + ' ') > 0

    IF (@@ROWCOUNT = 0)
        BREAK
END
4

2 に答える 2

2

ここでの最善のアプローチは、変更したデータをデータベースに保存することだと思います。IDとフォーマットされたアドレスを使用して別のテーブルを作成することも、現在のテーブルに列を追加することもできます。

次に、すでにたくさんのレコードがあるので、それらを更新する必要があります。ここでは、内部関数を作成して現在のレコードを更新するために使用するオプションが必要です(遅いかもしれませんが、終了するとデータはすでにテーブルにあります)、またはCLRプロシージャを作成して次の機能を使用します正規表現。

次に、新しく挿入されたレコードの場合、SQLまたはCLR関数を呼び出して現在挿入されているレコードを更新するAFTERINSERTTRIGGERを作成するのは非常に柔軟です。

于 2012-11-13T07:17:36.147 に答える
2

ばかげたことをいつでも実行でき、これをすべての置換をインラインで動的 SQL として実行できます。

declare @sql nvarchar(max)
set @sql = 'Street_Name'
select @sql = 'replace(' + @sql + ', '' ' + Word + ' '', '' ' + Replacement + ' '')'
from Word_Substitutions
set @sql = 'update Street_Addresses set Expanded_Name = ' + @sql
exec sp_executesql @sql

はい、私は1つか2つの反対票を完全に期待していますが、UDFと再帰CTEが大規模なデータセットで非常に遅くなる場合があることを考えると、この方法はうまく機能することがあります. また、時折、思いがけない解決策を投稿するのも楽しいものです。

とにかく、特に@gotqnによる保存とトリガーベースの更新の提案と組み合わせた場合(私は同意し、賛成しました)、これがどのように実行されるかを知りたいと思います.

私は現在、275 の置換単語と 100k のアドレスを控えめなボックスで約 3 秒間実行しています。

于 2012-11-13T21:58:11.403 に答える