CSVファイルを読み取り、テーブルのレコードを生成する方法をご紹介します。
はじめに
CSVファイルから直接テーブルのレコードを生成する方法のご紹介になります。
今回の方法は、「SQL.Import.Mgr.cls」を利用して、CSVをインポートします。
細かい制御はできませんが、CSVファイルの取り込みが簡単に行えるので利便性は高いです。
また、データクラスに余計な関数を増やす必要がないのもメリットです。
同じく簡易な方法でのCSVインポート処理については、下記を参照してください。
CSVファイルを読み込む処理に関しては、下記記事を参考にしてください。
CSVファイルをインポートする
前述した通り、CSVファイルをインポートするには「SQL.Import.Mgr.cls」を使用します。
サンプルは下記になります。
関数の解説は後程行います。
ClassMethod importCSV(fileName As %String, clsNm As %String, rowName As %String, rowType As %String = "", dlm As %String = ",", charSet As %String = "UTF8")
{
s (makeRflg,openFlg) = 0
try {
s mgr = ##class(%SQL.Import.Mgr).%New()
s mgr.FileName = fileName
s sNm = $p(clsNm, ".", 1, *-1)
, tNm = $p(clsNm, ".", *)
s mobj = ##class(%SQL.Manager.API).%New()
$$$ThrowOnError(mobj.CheckIdentifier(.sNm))
$$$ThrowOnError(mobj.CheckIdentifier(.tNm))
s mgr.ClassName = clsNm
s mgr.TableName = $tr(sNm,".","_")_"."_tNm
s mgr.IQN=$$$BuildIQN(sNm, tNm)
// CSVファイルの設定
s mgr.Delimiter = dlm
//s mgr.StringQuote = """"
s mgr.Charset = charSet
// 日時の設定
s mgr.DateFormat = 10 // 10:YYYY-MM-DD, YYYY/MM/DD
, mgr.TimeFormat = 1
, mgr.TimeStampFormat = 8 // YYYY-MM-DD hh:mm:ss
// 取り込み設定
s mgr.NoCheck = 0 // 1:validationを行わない
s mgr.HasHeaders = 1 // 1:ヘッダを含む(複数行ヘッダはNG)
s mgr.DeferIndices = 0 // 1:データインポート後にインデックス作成
// CSVカラムと型を設定
f pos=1:1:$l(rowName,",") d mgr.ColumnNames.Insert($p(rowName,",",pos))
i (rowType'="") f pos=1:1:$l(rowType,",") d mgr.ColumnTypes.Insert($p(rowType,",",pos))
$$$ThrowOnError(mgr.GenerateImportRoutine()) // 取り込みルーチン生成
s makeRflg = 1
$$$ThrowOnError(mgr.OpenImport()) // ファイルを開く
s openFlg = 1
i (mgr.HasHeaders) $$$ThrowOnError(mgr.ReadHeader(.header)) // ヘッダを飛ばす
s (total,rows,okCnt,ngCnt) = 0
d {
d mgr.ImportRows(.rows, .ok,, .done,, total)
s total = total + rows
, okCnt = okCnt + ok
} while('done)
zw ^IRIS.TempSQLImp($job)
w !!,$$$FormatText("総数:%1, 成功:%2, 失敗:%3 at %4", total, okCnt, mgr.ErrorCount(), $zdt($h,3,1))
$$$ThrowOnError(mgr.BuildIndices())
// 後処理
$$$ThrowOnError(mgr.CloseImport())
s openFlg = 0
$$$ThrowOnError(mgr.DeleteImportRoutine()) // 取り込みルーチン削除
}catch e {
w !,$System.Status.DisplayError(e.AsStatus())
d:(openFlg) mgr.CloseImport() // ファイルを閉じる
d:(makeRflg) mgr.DeleteImportRoutine() // ルーチン削除
}
}
解説
関数の引数を解説
引数名 | 説明 |
---|---|
fileName | CSVファイルのフルパス |
clsNm | 取り込み対象テーブルの名称 |
rowName | CSVファイルの列名を指定する ※データクラスのプロパティ名と一致させる必要があります |
rowType | CSVファイルの列の型を指定する ・D – Date ・TS – TimeStamp ・N – Numeric ・S – String、Stream ・T – Time |
dlm | デリミタ(初期値=”,”) |
charSet | ファイルの文字コード(初期値=”UTF8″) |
データクラスの各プロパティの型が、全て文字列であればrowTypeの指定は不要です。
もし、プロパティの型に「%Date」や「%Time」等が存在して、CSVファイルに出力されている場合は、rowTypeの指定を行わないと正確に取り込む事ができません。
取り込み処理の解説
兎にも角にも、先ずは「%SQL.Import.Mgr」をインスタンス化します。
s mgr = ##class(%SQL.Import.Mgr).%New()
プロパティ「ClassName 」「TableName 」「IQN」は必須の設定になります。
s mgr.ClassName = clsNm // データクラス名
s mgr.TableName = $tr(sNm,".","_")_"."_tNm // SQLで使用するクラス名
s mgr.IQN=$$$BuildIQN(sNm, tNm) // 内部用
「Delimiter」は、ファイルの区切り文字を指定します。
TSVファイル時は「$c(9)」を設定します。
各列の値が、何かしらの文字列で括られている場合、「StringQuote」に該当の文字を指定します。
例) [“テストの点数“, “96“, “合格“] ←であれば「“」をStringQuoteに指定する
ファイルの文字コードを「Charset」に指定します。
s mgr.Delimiter = dlm // デリミタ "," or $c(9)
s mgr.StringQuote = """" // 値を括っている文字 "123","455"の「"」等
s mgr.Charset = charSet // 文字コード
「DateFormat」は「10」でOKです。
※日付のフォーマットが「YYYY-MM-DD or YYYY/MM/DD」のみで、「YYYYMMDD」は不可。
※「DD/MM/YYYY」であれば「9」です。
「TimeFormat」も基本は「1」で良いです。※$zth(time,format)の「format」の値を指定します。
日時のフォーマットが「YYYY-MM-DD hh:mm:ss」であれば「TimeStampFormat」は「8」固定、「YYYY-MM-DDThh:mm:ss」であれば、「9」固定になります。
s mgr.DateFormat = 10 // 10:YYYY-MM-DD, YYYY/MM/DD
, mgr.TimeFormat = 1
, mgr.TimeStampFormat = 8
CSVファイルが信頼できる精度のデータであれば、「NoCheck」「DeferIndices」は、「1」を指定しても問題ありません。
こちらの方が処理が早くなります。
CSVファイルのデータに重複しているユニークキーが存在していたり、値の変換「日付の$horolog化」が必要であったり等の理由があれば、両プロパティは「0」を指定します。
ヘッダが設定されている場合は、「HasHeaders」に「1」を設定します。
※ヘッダは1行のみ有効です。複数行は未対応です。
s mgr.NoCheck = 0 // 1:validationを行わない
s mgr.HasHeaders = 1 // 1:ヘッダを含む(複数行ヘッダはNG)
s mgr.DeferIndices = 0 // 1:データインポート後にインデックス作成
カラム(列)の名称と型を設定します。
カラムの名称は、プロパティ名と完全一致である必要があります。
f pos=1:1:$l(rowName,",") d mgr.ColumnNames.Insert($p(rowName,",",pos))
i (rowType'="") f pos=1:1:$l(rowType,",") d mgr.ColumnTypes.Insert($p(rowType,",",pos))
取り込み処理のメイン部分になります。
エラーが発生したら、「^IRIS.TempSQLImp($job, [行数])」に書き込まれるので、zwriteで出力しています。
s (total,rows,okCnt,ngCnt) = 0
d {
d mgr.ImportRows(.rows, .ok,, .done,, total)
s total = total + rows
, okCnt = okCnt + ok
} while('done)
zw ^IRIS.TempSQLImp($job)
引数「rows」を足していくと、読み込んだ総レコード数となります。
引数「ok」を足していくと、読み込み成功したレコード数となります。
引数「done=1」で、最終行の読み込み完了となります。
取り込み部のミソ
レコードが追加される内部処理が下記になります。
この処理は、「mgr.GenerateImportRoutine()」で生成されたルーチンの一部になります。
&sql(INSERT INTO "developer_csv".Patient (xActive,zUpdateCount,dele,storeCd,patientId,漢字氏名,カナ氏名,ローマ字氏名,漢字旧姓,カナ旧姓,性別,生年月日,死亡日時,コメント,新患登録日,ABO血液型,RH血液型,未使用区分)
VALUES (:valray(1),:valray(2),:valray(3),:valray(4),:valray(5),:valray(6),:valray(7),:valray(8),:valray(9),:valray(10),:valray(11),:valray(12),:valray(13),:valray(14),:valray(15),:valray(16),:valray(17),:valray(18)))
これより、「%SQL.Import.Mgr」を利用してのCSVファイル取り込みは、下記のような特性があります。
おわり
今回はCSVファイルの取り込みをご紹介致しました。
「%SQL.Import.Mgr」を利用すれば、「取り込みPGを別途作成する必要がない」「データクラスに取り込み関数(import)を作成する必要もない」為、非常に使い勝手の良い方法だと思います。
ただし、データの更新(UPDATE)や削除(DELETE)等ができないため、データの無いテーブルを対象にするか、ユニークキーが重複しないデータのみを取り込む事になります。
業務の一環としてCSVファイルの取り込みを行い、データ更新が発生するのであれば、この手法は使えません。
システム開始時のマスタ作成やテストデータの生成等でご活用下さい。