Google Apps ScriptでSheets APIを使ったら爆速だった【GAS】
Twitterを見ていた所、こちらのサイトにて「SpreadsheetAppよりもSheets APIを使ったほうが全然早い」という耳寄りな情報が流れてきて、「そんな事ってあるの?」と思い、自分でテストしてみることにしました。もし、これが事実であれば特に大量のデータの集約などをする場合、標準のSpreadsheetAppを使うよりも、6分の制限のあるGoogle Apps Scriptに於いては速度を稼ぐ優良な手段となりえます。
ということで、実際につかって検証してみます。
目次
今回使用するスプレッドシート
- Sheets APIでアクセス - Google Spreadsheet
今回の元データは診療報酬情報提供サービスの医薬品マスタ(今年度と令和2年度)のデータを利用して、この2つのデータを結合したものを、コピペするといった事で、SpreadsheetAppとSheets APIの間で速度差を計測してみたいと思います。
医薬品マスタをtempdata1,tempdata2にそれぞれ貼り付けてください。今回はテストで5000件に押さえていますが、実データは22000件ほどあります。
ちなみに、Google Apps Scriptで速度を稼ぐ・6分の壁を突破する項目は以下のエントリーになるので合わせてご覧ください。
Sheets APIについて
概要
本来Sheets APIはGoogle Apps Scriptから利用する為のものというものではなく、様々な外部アプリやウェブサービスからAccess Tokenを使って認証し、操作する為の汎用のREST APIです。しかし、Google Apps Scriptからでも標準でサービスから「Google Sheets API」を使う事で利用する事が可能になっています(この手法はDrive APIを追加する際によく利用しますね)
よって様々な言語からREST APIの作法に従ってHTTPリクエストをする事で、Googleスプレッドシートを操作する事が可能になっています。特徴としては
- 様々なプログラミング言語からGoogleスプレッドシートのデータ操作を行う事が可能になる
- バッチ処理が可能で、一括処理で行わせる事が可能
- Google Apps Scriptから利用する場合は、Access Token取得のための認証作業や、リクエスト時に追加する必要が無い
故に、大容量データや複数シートを沢山扱う場合には、SpreadsheetAppではなく、Sheets APIのほうが全然早く処理が可能になる驚きの結果でした。こんなことなら前から使っておけばよかった。
リクエスト制限
GoogleのAPIは当然のようにリクエスト制限が設けられています。以下の制限があるので利用する場合は考慮する必要があります。
- 読み出しに関して、プロジェクト1日辺り制限は無制限。1分辺りは300リクエスト、ユーザ毎1分辺り60リクエスト
- 書き出しに関して、プロジェクト1日辺り制限は無制限。1分辺りは300リクエスト、ユーザ毎1分辺り60リクエスト
割当を超過すると、429エラー(Too Many Request)が返ってきますが、正直かなり上記の制限は緩いので、早々超過する事はないのではないかと思います。
ソースコード
今回は集める先の「シート1」そして、2つのtempdataシート。最低でも3回はスプレッドシートにアクセスしなければなりません。このパターンに於いて処理開始時の時刻と終了時刻との差を取って、どちらが処理コストが掛かるのか?を10回程度計測してみることにします。
データは5000件*2シート分、列数は35列分(セルの数に換算して、35万セル分に該当します)。
SpreadsheetAppの場合
//SpreadsheetAppの場合
function ssappexe(){
//現在時刻を取得する
let starttime = new Date();
//シートを取得
let ss = SpreadsheetApp.getActiveSpreadsheet();
let sheet = ss.getSheetByName("シート1");
let temp1 = ss.getSheetByName("tempdata1");
let temp2 = ss.getSheetByName("tempdata2");
//tempdataのデータを取得
let temp1data = temp1.getRange("A1:AI5000").getValues();
let temp2data = temp2.getRange("A1:AI5000").getValues();
//結合した配列をシート1に書き出し
let lastColumn = temp1data[0].length; //カラムの数を取得する
let lastRow = temp1data.length; //行の数を取得する
sheet.getRange(2,1,lastRow,lastColumn).setValues(temp1data);
let lastColumn2 = temp2data[0].length; //カラムの数を取得する
let lastRow2 = temp2data.length; //行の数を取得する
sheet.getRange(5002,1,lastRow2,lastColumn2).setValues(temp2data);
//終了時刻との差分を算出して、メッセージを表示
let endtime = new Date();
let diff = (endtime.getTime()-starttime.getTime()) / 1000;
SpreadsheetApp.getUi().alert(diff + "ミリ秒掛かった");
}
- SpreadsheetAppはシートの読み出しは1シート1リクエスト消費するので、API呼び出しコストがシートの数だけ比例してしまい、ここがボトルネックになる
- また、ちょいちょい「ページが応答しません」となって止まるケースに遭遇した。
- 全体的にコードが見にくい上に、書き出しはRangeに4点を指定して出力が必要であるため、分かりづらい
- どうも、最終行を調べるendrowを使ったり、配列を結合するArray.prototype.push.applyを使うと「ページが応答しない」状態になってしまうようだ。endrowを調べるコードは以下の通り。故に今回のコードでは最終行はわかってるので、getRangeで直書きしてる。
var endrow = Number(ss.getLastRow()) + 1;
Sheets APIの場合
事前準備
Google Apps ScriptでSheets APIを利用する場合には事前準備が必要です。以下の手順で追加します。
- スクリプトエディタの画面にて、左サイドのサービスの+をクリックする
- Google Sheets APIを選択して追加をクリックする
これで、SheetsにつなげてAPIリクエストを送ることが可能になります。
図:APIの追加をする必要があります。
ソースコード
//Sheets APIの場合
function ssapiexe(){
//現在時刻を取得する
let starttime = new Date();
//このファイルのIDを取得
let ss = SpreadsheetApp.getActiveSpreadsheet();
let ssid = ss.getId();
//tempdataのデータを取得
var response = Sheets.Spreadsheets.Values.batchGet(
ssid,
{ranges:
[
"tempdata1!A1:AI5000",
"tempdata2!A1:AI5000"
]
}
);
//resnponseからデータを取り出し
let temp1data = response.valueRanges[0].values;
let temp2data = response.valueRanges[1].values;
//シートに書き出しオプション
let option = {
valueInputOption: 'USER_ENTERED',
data: [
{
range: 'シート1!A2:AI5001',
values: temp1data,
},
{
range: 'シート1!A5002:AI10001',
values: temp2data,
},
]
};
//シートに一括書き出し
let response = Sheets.Spreadsheets.Values.batchUpdate(
option, ssid
);
//終了時刻との差分を算出して、メッセージを表示
let endtime = new Date();
let diff = (endtime.getTime()-starttime.getTime()) / 1000;
SpreadsheetApp.getUi().alert(diff + "ミリ秒掛かった");
}
- シートの読み込みも書き出しもValues.batchGetやValues.batchUpdateにて一括で処理が行えるため、リクエスト回数が1回で済みます。
- コード全体がスッキリしていて非常に読みやすいです。
計測結果(秒)
読み込みのみのテスト
| 回数 | SpreadsheetApp | Sheets API |
| 1 | 3.902 | 1.942 |
| 2 | 3.941 | 1.573 |
| 3 | 5.394 | 2.036 |
| 4 | 3.962 | 2.034 |
| 5 | 4.607 | 1.626 |
| 6 | 4.142 | 1.817 |
| 7 | 3.862 | 1.948 |
| 8 | 5.261 | 1.946 |
| 9 | 4.070 | 1.853 |
| 10 | 4.555 | 1.912 |
シート1に書き出しをせずに、変数に値を格納する所までの数値ですが、Sheets APIのほうが2.8倍ほど早いですね。ここまで差があると、SpreadsheetAppで大量データを処理するのは鬼門とも言えるかもしれません。
読み書きのテスト
| 回数 | SpreadsheetApp | Sheets API |
| 1 | 29.334 | 9.849 |
| 2 | 30.207 | 10.711 |
| 3 | 29.180 | 10.600 |
| 4 | 27.785 | 10.913 |
| 5 | 29.222 | 9.839 |
| 6 | 26.364 | 12.059 |
| 7 | 26.382 | 10.616 |
| 8 | 27.143 | 9.648 |
| 9 | 28.465 | 11.676 |
| 10 | 32.204 | 10.775 |
シート1への書き出しまで含めて計測しています。やはりSheets APIのほうが2.7倍ほど早い結果になりました。また、Sheets APIは確実高速で処理できるため、ページが応答しないといった事もない為、これは驚きの結果です。なんでGASのメソッドのほうが遅いのだろうか。。。


