0

私は現在、データを SQL に取り込むさまざまな方法で遊んでいます。昨日、BCP を使用して問題に遭遇しました。これは解決しましたが、あまり役に立たないエラー情報のために SSIS パッケージを使用していることを思い出しました。私が好きな作業方法では、データ行全体をステージング テーブルに (BCP または一括挿入を使用して) 固定幅または区切りでロードし、データ行を強制的に型指定するよりも操作する方がはるかに幸せだと思います。 SQL への途中の列。

そのため、データを宛先に挿入する前にデータを分割して検証 (データ型をチェック) し、不良データ行を別のテーブルに書き出すことができるアプローチを見つけて、それらをどうするかを決定できるようにしたいと考えています。 .

シナリオをシミュレートするスクリプトを作成しました。importedData テーブルは、BCP または BULK INSERT の出力になります。ImportedData からのすべてのデータは、最終的に Presenters または RejectedData テーブルのいずれかに入る必要があります。

かなりうまくスケーリングできるアプローチが必要です。実際の状況では、2,000 万行のデータを含む 40 列のようなものになる可能性があるため、一度に 10,000 行を処理するようなことをしなければならないと考えています。

SQL Server 2012 にはおそらく役立つ新しい try_parse 関数がありますが、2005 および 2008 マシンでこれを実行できるようにする必要があります。

IF OBJECT_ID (N'ImportedData', N'U') IS NOT NULL DROP TABLE dbo.ImportedData
CREATE TABLE dbo.ImportedData (RowID INT IDENTITY(1,1), DataRow VARCHAR(MAX))

IF OBJECT_ID (N'Presenters', N'U') IS NOT NULL DROP TABLE dbo.Presenters
CREATE TABLE dbo.Presenters (PresenterID INT, FirstName VARCHAR(10), LastName VARCHAR(10))

IF OBJECT_ID (N'RejectedData', N'U') IS NOT NULL DROP TABLE dbo.RejectedData
CREATE TABLE dbo.RejectedData (DataRow VARCHAR(MAX))

-- insert as fixed-width
INSERT INTO dbo.ImportedData(DataRow)
SELECT           '1  Bruce     Forsythe  '                               
UNION ALL SELECT '2  David     Dickinson '
UNION ALL SELECT 'X  BAD       DATA'                                  
UNION ALL SELECT '3  Keith     Chegwin   '                        

-- insert as CSV
/*INSERT INTO dbo.ImportedData(DataRow)
SELECT '1,Bruce,Forsythe'                               
UNION ALL SELECT '2,David,Dickinson'                                  
UNION ALL SELECT 'X,BAD,DATA'
UNION ALL SELECT '3,Keith,Chegwin' 
*/
---------- DATA PROCESSING -------------------------------

SELECT
    SUBSTRING(DataRow,1,3) AS ID,
    SUBSTRING(DataRow,4,10) AS FirstName,
    SUBSTRING(DataRow,14,10) AS LastName
FROM
    ImportedData


---------- DATA PROCESSING -------------------------------
SELECT * FROM ImportedData
SELECT * FROM Presenters
SELECT * FROM RejectedData
4

2 に答える 2

1

20M行のシナリオとパフォーマンスに関する懸念については、それについて詳しく見ていきましょう。

ステップ1、データベースに大きなファイルをロードします。ファイルシステムはディスクに移動し、そのすべてのデータを読み取ります。たぶん、Fusion-ioドライブのバンクに座っていて、iopsは問題ではありませんが、そのありそうもないシナリオを除けば、bcp / Bulk insert / ssis / .net/etcを介してディスクからそのデータを読み取るのにX時間を費やします。次に、同じデータをすべてテーブル挿入の形式でディスクに書き戻すことに時間を費やすことができます。

ステップ2、そのデータを解析します。これらのサブストリング操作の実行にCPU時間を費やす前に、データ行を識別する必要があります。マシンがRAMで適切にプロビジョニングされている場合、ImportedDataのデータページはメモリ内にある可能性があり、それらにアクセスするためのコストははるかに低くなります。ただし、すべてがメモリにあるわけではないため、そのデータを取得するために論理読み取りと物理読み取りの組み合わせが発生する可能性があります。これで、そのソースファイルを2回効果的に読み取ったので、利益はありません。

次に、データの分割を開始します。箱から出して、TSQLはトリム、左、右、およびサブストリングメソッドを提供します。CLRを使用すると、.NET文字列ライブラリにいくつかのラッパーメソッドを取得して、コーディング作業を簡素化できますが、コーディング効率とインスタンス化コストをトレードオフできます。最後に、答えが(tsqlとclr)は「状況によって異なります」という問題について読みました。コミュニティを知っていると衝撃的ですが、それは実際には文字列の長さと多くの要因に依存します。

最後に、値を解析して、それが正当な値であるかどうかを確認する準備が整いました。おっしゃるように、SQL 2012には、try_parsetry_convertがあります。解析は完全に新しいものですが、ロケール対応データ(GBでは01-02-05、2005年2月1日。米国では2005年1月2日。JPでは2001年2月5日)を処理する必要がある場合は非常に貴重です。2012を使用していない場合は、CLRラッパーを使用して独自のバージョンをロールできます。

ステップ3、エラー!誰かが悪い日付か何かで滑って、あなたのキャストは失敗します。何が起こるのですか?クエリが成功するか失敗するかのどちらかであるため、すべての行が失敗し、「文字列またはバイナリデータが切り捨てられる」や「日時を文字列から変換するときに変換に失敗する」などの非常に役立つエラーメッセージが表示されます。Nサイズのスライスのどの行ですか?あなたがそれを探しに行くまであなたは知りません、そしてこれは人々が通常パフォーマンスをさらに低下させるRBARアプローチに移るときです。または、セットベースのままにしようとしますが、挿入を試みる前に変換に失敗するシナリオに対して、ソースセットフィルタリングに対して繰り返しクエリを実行します。

私のタグからわかるように、私はSSISのような人ですが、それが機能する唯一のものであると考えるほどの偏見ではありません。ETLのアプローチがあるとしたら、私はそれを試したと思います。あなたの場合、独自のETLコード/フレームワークを開発するか、既存のもの( rhinoまたはreactive)を使用することで、パフォーマンスとスケーラビリティが大幅に向上すると思います。

最後に、varchar(max)の意味に注意してください。それに関連するパフォーマンスコストがあります。

また、説明したように、プロセスでは、ETLの単一のインスタンスのみを一度に実行できます。おそらくそれはあなたのユースケースをカバーしていますが、私たちが多くのETLを行った企業では、クライアントBにクライアントAのetlが処理を完了するのを待ってから作業を開始するように強制することはできませんでした。そうしないと、すぐにクライアントが不足します。

于 2012-08-24T19:17:11.460 に答える
0

T-SQL でこれを行う簡単な方法はありません。この場合、解析しようとするすべてのデータ型に対して isdate() 、isnumeric() 型の UDF が必要です。次に、拒否されたものを拒否されたテーブルに移動し、それらの行をimportdateから削除してから、ロードを続行できます..

SELECT 
RecordID,
SUBSTRING(DataRow,1,3) AS ID, 
SUBSTRING(DataRow,4,10) AS FirstName, 
SUBSTRING(DataRow,14,10) AS LastName,
SUBSTRING(DataRow,24,8) AS DOB,
SUBSTRING(DataRow,32,10) AS Amount,     
INTO RejectedData 
FROM ImportedData
WHERE  ISDATE(SUBSTRING(DataRow,24,8))= 0 
OR ISNUMERIC(SUBSTRING(DataRow,32,10))=0

次に、インポートされたデータから削除します

DELETE FROM ImportedData WHERE RecordID IN (SELECT RecordID FROM RejectedData )

次に、プレゼンターに挿入します

INSERT INTO Presenters     
SELECT 
RecordID,
SUBSTRING(DataRow,1,3) AS ID, 
SUBSTRING(DataRow,4,10) AS FirstName, 
SUBSTRING(DataRow,14,10) AS LastName,
CONVERT(Date,SUBSTRING(DataRow,24,8)) AS DOB,
CONVERT(DECIMAL(18,2),SUBSTRING(DataRow,32,10)) AS Amount,  
FROM ImportedData

挿入でバッチを管理する場合、これは非常に優れた記事です。

http://sqlserverplanet.com/data-warehouse/transferring-large-amounts-of-data-using-batch-inserts

于 2012-08-27T19:34:44.863 に答える