インポート内で一意のキーとして電話番号を使用するインポートユーティリティを作成しています。
電話番号がデータベースにまだ存在していないことを確認する必要があります。問題は、DB内の電話番号にダッシュや括弧などが含まれている可能性があることです。これらを削除する関数を作成しましたが、問題はslowで、DBに数千のレコードがあり、一度にインポートする数千のレコードがあることです、このプロセスは許容できないほど遅くなる可能性があります。電話番号の列を既にインデックスにしました。
この投稿のスクリプトを使用してみました:
T-SQL trim&nbsp(およびその他の非英数字)
しかし、それはそれをスピードアップしませんでした。
数字以外の文字を削除するより速い方法はありますか? 10,000から100,000のレコードを比較する必要がある場合にうまく機能する何か。
何をするにしても、実行する必要がありますfast。
更新
人々の反応を考えると、インポートユーティリティを実行する前にフィールドをクリーニングする必要があると思います。
インポートユーティリティを作成しているものの質問に答えるために、それはC#アプリです。現在、BIGINTとBIGINTを比較していますが、DBデータを変更する必要はなく、非常に小さなデータセット(約2000レコード)でパフォーマンスが低下しています。
BIGINTとBIGINTを比較すると、速度が低下しますか?
アプリのコード側をできる限り最適化しました(正規表現を削除し、不要なDB呼び出しを削除しました)。 SQLを問題の原因として特定することはできませんが、それでも問題はないと感じています。
誤解するかもしれませんが、データベース内の現在のデータの文字列を削除する2つのデータセットがあり、インポートするたびに新しいセットがあります。
既存のレコードを更新するために、SQLを使用します。これは一度だけ実行する必要があります。
ただし、インポートユーティリティを記述していると言ったので、SQLはこの種の操作に対して最適化されていません。これらの更新は、SQLではなくインポートユーティリティのコンテキストで行います。これは賢明なパフォーマンスです。ユーティリティは何で書いていますか?
また、私はプロセスを完全に誤解しているかもしれませんので、オフベースの場合は謝罪します。
編集:
最初の更新では、SQL Server 2005を使用している場合、CLR関数を試すことができます。ここに正規表現を使用した簡単なものがあります。パフォーマンスがどのように比較されるかわからないので、今すぐ簡単なテストを除いて自分でこれを使ったことはありません。
using System;
using System.Data;
using System.Text.RegularExpressions;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString StripNonNumeric(SqlString input)
{
Regex regEx = new Regex(@"\D");
return regEx.Replace(input.Value, "");
}
};
これがデプロイされた後、更新するには以下を使用できます。
UPDATE table SET phoneNumber = dbo.StripNonNumeric(phoneNumber)
T-SQLコードとPATINDEXでこのソリューションを見ました。私はそれが好きです :-)
CREATE Function [fnRemoveNonNumericCharacters](@strText VARCHAR(1000))
RETURNS VARCHAR(1000)
AS
BEGIN
WHILE PATINDEX('%[^0-9]%', @strText) > 0
BEGIN
SET @strText = STUFF(@strText, PATINDEX('%[^0-9]%', @strText), 1, '')
END
RETURN @strText
END
replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(string,'a',''),'b',''),'c',''),'d',''),'e',''),'f',''),'g',''),'h',''),'i',''),'j',''),'k',''),'l',''),'m',''),'n',''),'o',''),'p',''),'q',''),'r',''),'s',''),'t',''),'u',''),'v',''),'w',''),'x',''),'y',''),'z',''),'A',''),'B',''),'C',''),'D',''),'E',''),'F',''),'G',''),'H',''),'I',''),'J',''),'K',''),'L',''),'M',''),'N',''),'O',''),'P',''),'Q',''),'R',''),'S',''),'T',''),'U',''),'V',''),'W',''),'X',''),'Y',''),'Z','')*1 AS string
、
:)
関数を作成したくない場合、またはT-SQLで1つのインライン呼び出しだけが必要な場合は、次を試すことができます。
set @Phone = REPLACE(REPLACE(REPLACE(REPLACE(@Phone,'(',''),' ',''),'-',''),')','')
もちろん、これは文字列関数からすべての特殊文字を削除する一般的なものではなく、電話番号のフォーマットを削除するためのものです。
シンプルな機能:
CREATE FUNCTION [dbo].[RemoveAlphaCharacters](@InputString VARCHAR(1000))
RETURNS VARCHAR(1000)
AS
BEGIN
WHILE PATINDEX('%[^0-9]%',@InputString)>0
SET @InputString = STUFF(@InputString,PATINDEX('%[^0-9]%',@InputString),1,'')
RETURN @InputString
END
GO
create function dbo.RemoveNonNumericChar(@str varchar(500))
returns varchar(500)
begin
declare @startingIndex int
set @startingIndex=0
while 1=1
begin
set @startingIndex= patindex('%[^0-9]%',@str)
if @startingIndex <> 0
begin
set @str = replace(@str,substring(@str,@startingIndex,1),'')
end
else break;
end
return @str
end
go
select dbo.RemoveNonNumericChar('aisdfhoiqwei352345234@#$%^$@345345%^@#$^')
明らかな理由から、varcharsの操作は、数値の操作と比較して基本的に遅く、非効率的です。元の投稿でリンクする関数は、文字列内の各文字をループ処理して数字かどうかを判断するため、実際には非常に遅くなります。それを何千ものレコードに対して行うと、プロセスは遅くなるはずです。これは正規表現に最適な仕事ですが、SQL Serverではネイティブにサポートされていません。 CLR関数を使用してサポートを追加できますが、これを試すことなくこれがどれほど遅いかを言うのは難しいですが、各電話番号の各文字をループするよりも大幅に高速であると確信しています!
データベース内で電話番号をフォーマットして数字だけにする場合、SQLの数値型に切り替えると、他の数値型との比較が非常に高速になります。新しいデータの入力速度に応じて、比較対象が適切にフォーマットされていれば、データベース側でトリミングと数値への変換が十分に高速であることがわかりますが、可能であれば、データベースにアクセスする前にこれらのフォーマットの問題を処理する.NET言語でインポートユーティリティを記述しないでください。
いずれにせよ、オプションのフォーマットに関して大きな問題が発生します。番号がOriginで北米のみであることが保証されている場合でも、完全に市外局番の修飾された電話番号の前に1を置く人とそうでない人は、同じ電話番号の複数のエントリの可能性を引き起こします。さらに、データの内容によっては、複数の人が住んでいる自宅の電話番号を使用する人もいるため、一意の制約により、世帯ごとにデータベースメンバーが1人しか許可されません。作業番号を使用して同じ問題を抱える人もいれば、人為的な一意性の可能性を再び引き起こす拡張子を含めるか含めない人もいます。
これらはすべて、特定のデータと使用状況に応じて、影響を与える場合と与えない場合がありますが、留意することが重要です!
最初にScottのCLR関数を試しますが、更新されるレコードの数を減らすためにWHERE句を追加します。
UPDATE table SET phoneNumber = dbo.StripNonNumeric(phoneNumber)
WHERE phonenumber like '%[^0-9]%'
レコードの大部分に数字以外の文字が含まれていることがわかっている場合は、役に立たない可能性があります。
夜間のプロセスでそれらを削除して別のフィールドに保存し、プロセスを実行する直前に変更されたレコードを更新できますか?
または、挿入/更新で、後で参照するために「数値」形式を保存します。トリガーはそれを行う簡単な方法です。
ゲームに遅れていることはわかっていますが、ここではT-SQL用に作成した、数字以外の文字をすばやく削除する関数を示します。注目すべきは、文字列のユーティリティ関数を配置するスキーマ「文字列」があります...
CREATE FUNCTION String.ComparablePhone( @string nvarchar(32) ) RETURNS bigint AS
BEGIN
DECLARE @out bigint;
-- 1. table of unique characters to be kept
DECLARE @keepers table ( chr nchar(1) not null primary key );
INSERT INTO @keepers ( chr ) VALUES (N'0'),(N'1'),(N'2'),(N'3'),(N'4'),(N'5'),(N'6'),(N'7'),(N'8'),(N'9');
-- 2. Identify the characters in the string to remove
WITH found ( id, position ) AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY (n1+n10) DESC), -- since we are using stuff, for the position to continue to be accurate, start from the greatest position and work towards the smallest
(n1+n10)
FROM
(SELECT 0 AS n1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS d1,
(SELECT 0 AS n10 UNION SELECT 10 UNION SELECT 20 UNION SELECT 30) AS d10
WHERE
(n1+n10) BETWEEN 1 AND len(@string)
AND substring(@string, (n1+n10), 1) NOT IN (SELECT chr FROM @keepers)
)
-- 3. Use stuff to snuff out the identified characters
SELECT
@string = stuff( @string, position, 1, '' )
FROM
found
ORDER BY
id ASC; -- important to process the removals in order, see ROW_NUMBER() above
-- 4. Try and convert the results to a bigint
IF len(@string) = 0
RETURN NULL; -- an empty string converts to 0
RETURN convert(bigint,@string);
END
次に、これを使用して、次のような挿入の比較を行います。
INSERT INTO Contacts ( phone, first_name, last_name )
SELECT i.phone, i.first_name, i.last_name
FROM Imported AS i
LEFT JOIN Contacts AS c ON String.ComparablePhone(c.phone) = String.ComparablePhone(i.phone)
WHERE c.phone IS NULL -- Exclude those that already exist
パフォーマンスの観点からインライン関数を使用します。以下を参照してください。「+」、「-」などの記号は削除されないことに注意してください
CREATE FUNCTION [dbo].[UDF_RemoveNumericStringsFromString]
(
@str varchar(100)
)
RETURNS TABLE AS RETURN
WITH Tally (n) as
(
-- 100 rows
SELECT TOP (Len(@Str)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
)
SELECT OutStr = STUFF(
(SELECT SUBSTRING(@Str, n,1) st
FROM Tally
WHERE ISNUMERIC(SUBSTRING(@Str, n,1)) = 1
FOR XML PATH(''),type).value('.', 'varchar(100)'),1,0,'')
GO
/*Use it*/
SELECT OutStr
FROM dbo.UDF_RemoveNumericStringsFromString('fjkfhk759734977fwe9794t23')
/*Result set
759734977979423 */
100文字以上で定義できます...
通常、数千のレコードに対する数千のレコードは問題になりません。 SSISを使用して、このような重複排除を使用して何百万ものレコードをインポートしました。
データベースをクリーンアップして、最初に数字以外の文字を削除し、それらを除外します。
超シンプルなソリューションを探しています:
SUBSTRING([Phone], CHARINDEX('(', [Phone], 1)+1, 3)
+ SUBSTRING([Phone], CHARINDEX(')', [Phone], 1)+1, 3)
+ SUBSTRING([Phone], CHARINDEX('-', [Phone], 1)+1, 4) AS Phone
「SQLを問題の原因として特定することはできなくなりましたが、それでもそのように感じます。」
SQLプロファイラーを起動して見てみましょう。結果のクエリを取得し、実行プランをチェックして、インデックスが使用されていることを確認します。