FileMakerでJSONデータでの転記速度を比較してみた

#JSONでデータ転記は速いのか?

[前回](https://qiita.com/calafate40/items/56c2de2ab99a1f872b3b)、JSONデータを計算式で各レコードに持たせてデータ転記に使うという記事をアップしたのですが、実際それが高速なのか検証してみました。

##検証に使ったのは以下のスクリプト達

1. 連続レコードの作成　基準値として使用
2. 各レコードに計算値として持たせたJSONデータを使用したJSON_1
3. 全レコードをLOOPして全てをJSONデータ化して転記するJSON_2
4. ExecuteSQLを使った転記
5. 中間ファイルのPivotを使った転記

***
##それぞれのスクリプトの説明

####1は単純にポータル行に一つだけデータを入れた伝票データの連続作成　速度の基準として利用
![レイアウト.png](https://qiita-image-store.s3.amazonaws.com/0/171037/c0870d28-0abf-18e1-442a-0b69a14bdfb5.png)


####2は[前回記事](https://qiita.com/calafate40/items/56c2de2ab99a1f872b3b)に書いた計算値で各レコード毎に事前にJSONデータを持たせておいて、そのデータを使った転記

	* JSONデータを持った計算値フィールドを改行区切りでリスト化して引数として渡す。
	* 受け取った側はその改行区切りデータを一行づつ分解して一つのテキストフィールドに入れていく。
	* そのJSONデータが入ったフィールドをJSONGetElementで参照して各フィールドを埋めていく。

####3はスクリプト発動時に全レコードをLOOPしてJSONデータ化し、それを引数として転記先ファイルに渡す。

	* 全レコードをLOOPしてJSONデータを作成して引数に。
	* 受け取った側はJSONGetElementでその引数から各フィールドを埋めていく。
	* 上記2のようにレコード毎にあるJSONフィールドに一旦落とし込むことはせず、受け取った引数をJSONGetElementで参照しています。
	
####4はオーソドックスなExecuteSQLを使った転記

	* 引数で伝票の主要フィールドをリスト化して転送先に渡し、新規レコードで追加。GetValueで各フィールドを埋めていく。
	* ポータルの内容は伝票IDを元にExecuteSQLでリスト化し、転送先では明細レイアウトに切り替えて新規レコードとして追加していく。
	* 上の2つを1レコード毎に繰り返す。

####5はFileMaker公式の[同期ガイド](http://fmdl.filemaker.com/kk/ios/docs/FileMaker_Sync_Guide_JP.pdf)に公開されている、中間テーブルのPivotを使った転記方法を参考にしたもの

	* 中間テーブル（新たに中間ファイルを作成してもよいし、伝票もしくは転記先ファイルの中に新規テーブルとして作成してもよい）を用意し、伝票IDのみを入れるグローバルフィールドを一つ用意
	* そのグローバルフィールドと伝票IDをリレーション
	* 同じくそのグローバルフィールドと転記先の伝票IDをリレーション（このリレーションで新規レコードの作成を許可オン）
	* 中間テーブルのグローバルフィールドを中心として、左手に転送元を、右手に転送先を繋いだ状態になります。
	* 中間テーブルのグローバルフィールドに伝票IDを次々に入れていき、都度、転記元の伝票情報を、転記先に割り当てていく方法です。リレーションの特性を上手く利用した転記方法です。
	* これを全レコード分LOOP
	

###5の方法の具体的イメージ
このPivot形式は文章では説明が難しいのでイメージを。こういうの考案する人凄い。
レイアウトはこんな感じでg_pivotを中心として左に転記元、右に転記先。こうやると理解しやすい。
![レイアウト図.png](https://qiita-image-store.s3.amazonaws.com/0/171037/478a0dca-3878-43e4-7354-ccbf0e54e515.png)

リレーションはこうなっています。
![リレーション.png](https://qiita-image-store.s3.amazonaws.com/0/171037/87e81400-a464-5fb4-6ed3-9e36db595498.png)

このリレーションを使用して、このテーブルでのレコード作成を許可にチェック
![許可.png](https://qiita-image-store.s3.amazonaws.com/0/171037/1d9b91b7-4ef6-bef8-aeeb-d3d62e7898c9.png)
	

#予想は外れた
予想では既に一部のデータができている2のJSON_1が一番高速だと思っていました。そして一番遅いのが5の中間ファイルを使った転送かなと。バケツリレーのように見えますから。でもスクリプトは一番短い。

結果は予想外で、5の中間ファイルを使った方法が一番高速でした。4のSQLと2のJSON_1が同等。3の一からJSONデータ作成するのは問題外という結果。もちろん自分のスクリプトが雑で足を引っ張っている部分もあるのかもしれませんが。スクリプト長くなったし。
![スクリーンショット 2018-03-17 14.35.07.png](https://qiita-image-store.s3.amazonaws.com/0/171037/aeb3007e-c5c3-1320-10da-d00e8768f3c7.png)

* 100レコード毎に測定
* 1000レコードのデータ転送で一番速いのが5のPivot方式
* 2のJSON_1と4のSQLが同等。ExecuteSQLはこういう場面ではほんと速い。
* 3のJSON_2が2より遅いのは当然ですが、JSONGetElementでの取り出し方法を変えているだけで大幅に速度低下。この辺はやり方で改善するはず。
* 3のJSON_2はJSONSetElementで全レコードを束ねるところで止めると1,000レコードで11秒弱。この段階で既に最下位でした。
* 一度に大量のテキストの塊を作るのはダメということですね。これはJSONとは関係なく。
* テーブル間を行ったり来たりするのは感覚的には遅そうですが、マシンにとっては関係ないのですね。

#JSONで大量のデータを処理するには（仮説）

* JSONGetElementで取得するのが意外と鈍足のようです。私見ですが。
* JSONデータをFileMakerで大量処理する場合は、小さなオブジェクト毎に一旦分解し、それを再分解した方がいいのでしょうかね。

試しに10,000レコードまで増やして2のJSON_1スクリプトで単純にJSONフィールド一つのみを転記してみましたが、たった1フィールドですから転送に44秒。（同じ10,000レコードで1の新規レコードの作成は30秒。5のPivotが53秒。Pivot優秀ですね）。

ということはJSONで大量のデータ転記する場合は、まずデータ受取用のテーブル（中間テーブル）でも作成し、そこに1レコード1テキストフィールドで送られてきたデータを挿入するだけにして一旦終わり。別処理として転記先からそのデータ受取用のテーブルを参照してJSONGetElementで受け取っていくという2段階方式が良さそうです。中間テーブルにJSON形式で保持しておけば後から再読み込みも簡単ですし。
まああくまでも大量のデータ一括処理ですので、数百件程度なら3の頭からJSONデータ化する方法以外は何をやっても気になりません。

#JSONへの拘り
自分なりの検証の結果、現状はあえてJSONに拘らなくても速度的メリットはないとわかりました。
ただ、データ転送時にJSON化できるのであれば、他システムとの連携・バックアップとして有用だなと。リレーション・レイアウトに依存しないのでメンテナンスが楽ですし。

##参考
データ転送の参考には高岡氏のこの映像と公式同期ガイドが非常に勉強になりました。最初は理解するの大変でしたが・・・。
[FileMaker Go と FileMaker Server のデータ同期のベストプラクティス](https://www.youtube.com/watch?v=GYXYZVx-nlw)

#追記
FMCommunityの海外ディスカッションで[同じようなTopics](https://community.filemaker.com/thread/182573)見つけました。やはりJSONデータは細かく細分化してから処理すべしと。