【スプレッドシート × GAS】スプレッドシートへのアクセススピードについて

GAS からスプレッドシートへのアクセスが遅い?

GASを利用していると、ほとんどのプロジェクトでスプレッドシートも一緒に使います。
この時、スプレッドシートへの書き込みや読み込みに時間がかかることがあります。
要因は様々ですが、その中の一つとしてスプレッドシートへのアクセスが頻繁に行われているからかもしれません。
ということで、スプレッドシートへのアクセススピードをチェックしてみます。

GAS でスプレッドシートへアクセスする方法

まずは GAS でスプレッドシートの値を取得してみます。

// データを全て取得する
function getAllData() {
  // シート名
  const DATA_SHEET_NAME = 'data';
  // 紐付いているスプレッドシートの DATA_SHEET_NAME シートを取得
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(DATA_SHEET_NAME);
  // 全てのデータを返す
  return sheet.getDataRange().getValues();
}

また、計測用の関数を用意しておきます。

// 計測用の関数
function main() {
  console.time("MAIN");
  // ここに計測したい処理を書く
  console.timeEnd('MAIN');
}

1万行5列と2万行5列のデータへのアクセススピード

ダミーデータを用意して、スプレッドシート上のデータを全て取得してみます。
まずは1万行5列のデータから。

次に2万行5列のデータを取得してみます。

実行するタイミングによっても変化する可能性があるので、一度の関数呼び出しで、順番を入れ替えて実行してみました。

1万行→2万行

2万行→1万行

1万行5列のデータを全て取得するのにかかった時間は約3秒(3,000ms)
2万行5列のデータを全て取得するのにかかった時間は約6秒(6,000ms)
となっています。

検索してみる

試しに検索してみましょう。
TextFinderClass を利用した関数と自分で作成した mySearchData 関数で比べてみます。

// 検索処理 TextFinder を使ってみる
function searchData(TARGET_SHEET_NAME, word) {
  // 検索対象のシート名
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(TARGET_SHEET_NAME);
  const cells = sheet.createTextFinder(word).findAll();
  console.log(cells.length);
}
// 自作してみる
function mySearchData(TARGET_SHEET_NAME, word) {
  // 検索対象のシート名
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(TARGET_SHEET_NAME);
  const allData = sheet.getDataRange().getValues();
  const found = [];
  for(let i = allData.length - 1; 0 <= i; i --) {
    const row = allData.join(' ');
    if(row.indexOf(word) === -1) continue;
    found.push(allData[i]);
  }
  console.log(found.length);
}

実行結果

TextFinder

自作

1万行→2万行の順で検索、2万行→1万行の順で検索した時、1万行よりも2万行の方が検索に時間がかかるかと思いきや、先に開くシートによって速度が変わるようです。
おそらく、GASの実行時にサーバー側で最適化がされているようです。
ソースコードは左側なのですが、サーバー側では右側のように実行されているように感じます。

flowchart TD GASの実行 --> A[SpreadsheetAppでスプレッドシートを開く] --> 1万行シートの取得 --> B[シートデータの取得] --> 1万行の検索処理 --> SpreadsheetAppでスプレッドシートを開く --> 2万行シートの取得 --> C[シートデータの取得] --> 2万行の検索処理
flowchart TD GASの実行 --> A[SpreadsheetAppでスプレッドシートを開く] --> 1万行シートの取得 --> B[シートデータの取得] --> 1万行の検索処理 A --> 2万行シートの取得 --> C[シートデータの取得] --> 2万行の検索処理

試しにそれぞれ別々に実行してみると、

TextFinder では、1万行・2万行、共に約0.5秒
自作した関数では、1万行で約0.3-0.5秒・2万行で約0.4-0.8秒
程でした。
※時間を置いて実行した一回目はスプレッドシートにアクセスするのに時間がかかるせいか、1-2秒かかることもありました。

書き込みしてみる

100行5列のデータを追記してみました。

// 呼び出し
function main() {
  const testData = ["適当なデータ"]; // テストデータ 今回は100行のデータで試してみました
  const TARGET_SHEET_NAME = 'writeData';
  console.time('1行ずつ追加');
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(TARGET_SHEET_NAME);
  const allData = sheet.getDataRange().getValues();
  for(let i = testData.length - 1; 0 <= i; i --) {
    allData.push(appendOneRow(testData[i], allData, sheet));
    break;
  }
  console.timeEnd('1行ずつ追加');
  console.time('まとめて追加');
  appendRows(TARGET_SHEET_NAME, testData);
  console.timeEnd('まとめて追加');
}
// 1行追記する
function appendOneRow(appendData, allData, sheet) {
  const TARGET_SHEET_NAME = 'writeData';
  sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(TARGET_SHEET_NAME);
  allData = sheet.getDataRange().getValues();
  appendData[0] = allData[allData.length - 1][0] + 1;
  sheet.appendRow(appendData);
  return appendData;
}
// 複数行追記する
function appendRows(TARGET_SHEET_NAME, data) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(TARGET_SHEET_NAME);
  const allData = sheet.getDataRange().getValues();
  const lastIndex = allData[allData.length - 1][0] + 1;
  const appendData = data.map((el, index) => {
    el[0] = lastIndex + index;
    return el;
  });
  sheet.getRange(allData.length + 1, 1, appendData.length, appendData[0].length).setValues(appendData);
}

一行ずつの書き込み処理では、Sheetオブジェクトの appendRow()を呼び出して追記しています。
appendRow() は呼び出すと、シートの最後の行に引数に渡した配列を追記してくれる関数です。
つまり、呼び出すたびにスプレッドシートにアクセスしています
すると、1行追記するだけなら時間がかからない処理でも、100行、1000行追記するとなると、時間がかかってしまいます。

複数行まとめて追記する処理のほうも見てみます。
こちらは、Sheetオブジェクトから追記したい範囲(Range)を取得し、RangeオブジェクトのsetValues()に二次元配列の引数を渡して追記しています。
追記したいデータの行数とデータの列数分の範囲を指定する手間はありますが、範囲内に二次元配列を一度に追加できます。
つまり、データの行数・列数に関係なく、追記する一度だけスプレッドシートにアクセスしています

*試しにまとめて1000行追記してみました。

まとめ

ということで、スプレッドシートからデータの取得・検索・追記の3点からスプレッドシートへのアクセススピード、処理速度について簡単に調べてみました。
スプレッドシートに何度もアクセスするような処理は避けたほうが良さそう、でした。
てっきり、スプレッドシートを何度も開く処理をすると、処理が遅くなるかと思いきや、サーバー側で処理の最適化されているようなので、遅くならずに意外でした。
もしかすると、別のスプレッドシートを複数開くと処理速度が変わるかもしれません。


ソースコードの読みやすさや、バグになりかねない処理は避けたい気持ち、処理速度、

何を選ぶか

なので、どれが正解は人によります。

*このサイトを参考に何かを作成した際に起こった全ての責任を負いません*