大量データのロードを高速化する方法
これはInterSystems FAQ サイトの記事です。
インデックスが複数定義されているクラス/テーブルへ csv 形式等のシーケンシャルファイルから大量データをデータベースに登録する際、推奨される登録方法として、データ登録時インデックスを生成させず、登録完了後に一括でインデックスを生成する 方法があります。
この方法は、新規に大量のレコードを一括登録する際に最も有効な手段となります。
<メモ>
大量のデータを追加登録する際には、既存のデータ量と新規データ量のバランスにより、この手法が有効でないケースもあります。その場合は、インデックスの再構築を範囲指定で行うこともできます。
説明に使用するクラス定義例は以下の通りです。
Class ISJ.QL2 Extends %Persistent
{
Property Name As %String;
Property Title As %String;
Property Sex As %String;
Property Company As %String;
Property Phone As %String;
Property City As %String;
Property State As %String;
Property Zip As %String;
Index NameIndex On Name;
Index CompanyIndex On Company;
Index PhoneIndex On Phone;
}
データロードを行うクラスメソッド例は以下の通りです。
ClassMethod ImportFromFile(pFile As %String) { #dim Err As %Exception.AbstractException //埋め込みSQLを使用してインポート Try { if $get(pFile)="" { write "インポートファイルを指定してください",! quit } if ##class(%File).Exists(pFile)=0 { write "指定したファイルは存在しません。ファイル名、パスを確認してください",! quit } set filestream=##class(%Stream.FileCharacter).%New() do filestream.LinkToFile(pFile) set tDelim=";" while filestream.AtEnd=0 { //改行があるところまでRead set tLine=filestream.ReadLine() set pCity = $Piece(tLine,tDelim,1) //City set pCompany = $Piece(tLine,tDelim,2) //Company set pName = $Piece(tLine,tDelim,3) //Name set pPhone = $Piece(tLine,tDelim,4) //Phone set pSex = $Piece(tLine,tDelim,5) //Sex set pState = $Piece(tLine,tDelim,6) //State set pTitle = $Piece(tLine,tDelim,7) //Title set pZip = $Piece(tLine,tDelim,8) //Zip &sql(INSERT %NOINDEX INTO ql2 (Name, Title, Sex, Company, Phone, City, State, Zip) values (:pName, :pTitle, :pSex, :pCompany, :pCity, :pCity, :pState, :pZip)) // SQL文でエラーがある場合の処理 if SQLCODE<0 { throw ##class(%Exception.SQL).CreateFromSQLCODE(SQLCODE,%msg) } } // 最後にテーブルのインデックス再構築を実行 set st=..%BuildIndices() $$$THROWONERROR(Err,st) // エラーが発生した場合Catchへ移動 } Catch Err { write "エラーが発生しました",! write Err.DisplayString(),! } }
クラスメソッドでは、以下のデータ形式で作成されたファイルを入力しながらデータ登録後にインデックスを構築しています(ランダム生成させたデータを使用しています)。
;札幌市西区;NTS工業 株式会社;本田,亮;0325-6753-6990;男;北海道;アシスタント管理者;0630003
;呉市;セコミ薬品 株式会社;川原,明雄;0670-9635-5468;男;広島県;副衛生士;7370145
;春日部市;SESコミュニケーションズ 株式会社;大島,江美;0407-3421-5865;女;埼玉県;研究ディレクタ;3440065
;上高井郡小布施町;電金証券 有限会社;松本,亮;053-3208-4665;女;長野県;副会計士;3810202
;茅野市;ビーエスシ薬品 株式会社;渡部,弘明;0996-5061-8567;女;長野県;国際マーケティングマネージャ;3910212
;中川郡豊頃町;電金技研 有限会社;根本,由貴;0647-5142-4961;男;北海道;国際ウェブマスタ;0895461
;尼崎市;SES石油 有限会社;川口,博美;0744-3148-1523;男;兵庫県;副会計士;6610978
;北松浦郡吉井町;三友製造 株式会社;阿部,陽子;0554-2270-3308;男;長崎県;副システムエンジニア;8596304
サンプルコードの以下の文で、全レコードに対してインデックスを構築しています。
Do ..%BuildIndices()
サンプルコードで使用している埋め込み SQL では、実行後に %ROWID 変数を使用して INSERT によって設定された ID 値を取得できます。
例えば、INSERT したレコードのインデックスのみを構築したい場合は、%BuildIndices() メソッドの第 2 引数(pAutPurge)に 0 を指定し、第 5 引数(pStartID)および第 6 引数(pEndID)に ID を指定することで実行できます。
インデックス構築方法について詳細は、ドキュメントもご参照ください。