GASでセルを1個ずつ書くと遅くなる。setValues してみよう

GAS

書くたびにシートへ通信している

Google Apps Script(GAS)を使ってスプレッドシートを操作する際、データの書き込み処理は非常に頻繁に行われます。しかし、データ量が増えるにつれてスクリプトの実行時間が長くなり、場合によってはタイムアウト(6分の壁)のエラーになってしまうことがあります。

このような問題を解決するためのベストプラクティスが、setValuesメソッドを使った「一括書き込み」です。二次元配列のデータをスプレッドシートに一括で書き込む目的と、その具体的な実装コード、さらには実務での応用シーンについて初心者にもわかりやすく解説します。

3行2列を一度に置く

まずは、次のコードです。以下のコードをエディタに置いて動かせば、二次元配列のデータをスプレッドシートへ一括で書き込むことができます。

const sheet = SpreadsheetApp.getActiveSheet();
const data = [["ID", "名前"], [1, "田中"], [2, "佐藤"]];
sheet.getRange(1, 1, data.length, data[0].length).setValues(data);

範囲の縦横を配列から決める

GAS上のコードを1行ずつ見ます。

  • 1行目:const sheet = SpreadsheetApp.getActiveSheet();
    現在アクティブになっている(スクリプトに紐づく)スプレッドシートのシートオブジェクトを取得し、変数sheetに格納します。ここが書き込みのターゲットとなります。
  • 2行目:const data = [["ID", "名前"], [1, "田中"], [2, "佐藤"]];
    スプレッドシートに書き込みたいデータを二次元配列として定義します。外側のブラケット[]の中に、行ごとのデータとなる内側の[]がカンマ区切りで格納されています。1つ目の配列がヘッダー行、2つ目以降がデータ行に相当します。
  • 3行目:sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
    書き込み先のセル範囲を指定して、データを一括挿入します。getRange(行番号, 列番号, 行数, 列数)という構文になっており、1行目の1列目(A1セル)を起点としています。data.lengthで配列全体の行数(3)、data[0].lengthで最初の配列の列数(2)を動的に計算して範囲を確定させ、最後にsetValues(data)でスプレッドシートへ流し込みます。

範囲と配列のサイズを揃える

一括書き込みを実装する上で、先に揃えておくルールと、その裏にあるメカニズムを解説します。ここを理解していないと、思わぬエラーに悩まされることになります。

1. サイズ一致の原則(エラーを防ぐ最大のポイント)

setValuesを使用する際の最も重要な仕様が「サイズ一致の原則」です。getRangeで指定する書き込み先範囲の「行数・列数」と、引数としてセットする二次元配列(data)の「縦横サイズ」が完全一致していないと例外エラーが発生して処理が停止します。

例えば、配列データが3行×2列であるにもかかわらず、getRangeで3行×3列の範囲を指定してしまうとエラーになります。これを防ぐために、固定の数値を直接ハードコーディングするのではなく、前述のコードのようにdata.lengthとdata[0].lengthを使って、配列のサイズから動的に範囲を取得する手法がプログラミングの鉄則です。

2. なぜsetValueではなくsetValuesを使うのか?(実行時間短縮)

GASには単一のセルに書き込むためのsetValueメソッドも存在します。しかし、複数のデータを書き込む際にsetValueをループ処理(for文など)で繰り返すことは、パフォーマンス上、重大な悪手とされています。

GASからスプレッドシートへのアクセスは、外部APIを呼び出すのと同じくらい通信コストが高い処理です。セル単位のsetValueを1000回繰り返すと、1000回のAPI呼び出しが発生し、処理に数分かかることも珍しくありません。対して、メモリ上で配列にデータを1行だけでも二重の配列にするてからsetValuesを1回実行すれば、API呼び出しを最小化(1回のみ)することができ、処理時間は劇的に短縮されます。これがGASにおける実行時間短縮の理由です。

書き込みメソッド API呼び出し回数(1000件の場合) 処理速度の目安 適した用途
setValue 1000回(ループ処理時) 数分(タイムアウトのリスク大) 単一セルの更新、特定セルのフラグ変更
setValues 1回 数秒〜十数秒 大量データの新規登録、CSVデータの展開、レポート作成

APIの結果をいったん配列に積む

setValuesを使った一括書き込みは、実務のあらゆるデータ処理シーンで活躍します。よく使うのは次の書き方です。

二次元配列を作るための実務的なアプローチ

実際の開発現場では、最初から配列の中身が決まっていることは稀です。多くの場合、空の配列を用意し、ループ処理の中でデータを追加(push)していく手法を取ります。

// APIなどで取得した生データ(オブジェクトの配列)のイメージ
const rawData = [
  { id: 1, name: "田中", age: 28 },
  { id: 2, name: "佐藤", age: 35 },
  { id: 3, name: "鈴木", age: 42 }
];

const data = []; // 書き込み用の空の二次元配列を初期化
data.push(["ID", "名前", "年齢"]); // 1行目にヘッダー行を追加

for (let i = 0; i < rawData.length; i++) {
  // 各オブジェクトから必要なデータを抽出し、一次元配列としてpush
  data.push([rawData[i].id, rawData[i].name, rawData[i].age]);
}

// 最終的に data を getRange().setValues() で一括書き込み

このように、データの生成(配列への格納処理)と、スプレッドシートへの出力処理(setValues)を明確に分離することが、保守性の高いGASのコードを書くための秘訣です。

他システムからのCSVやAPIデータの取り込み

外部のWeb APIからJSONデータを取得したり、外部システムからエクスポートしたCSVファイルを読み込んだりしてスプレッドシートに転記するケースは非常に多いです。取得した大量のデータをループ処理でパースして巨大な二次元配列を構築し、最後にsetValuesで一気にスプレッドシートへ出力することで、数万件のデータであっても数秒で処理を完了させることが可能です。

大量データの集計とレポート自動作成

スプレッドシート上にある売上データなどの元データをgetValuesで一括取得し、GAS側で条件分岐や集計計算を行います。計算結果を新しい二次元配列として生成し、レポート用の別シートにsetValuesで一括出力することで、重たい関数をシート上に大量に記述することなく、サクサク動くレポートダッシュボードを自動生成できます。

行の列数が揃っていない

setValuesを使う際につまずきやすい代表的なエラーとその対処法を1行だけでも二重の配列にするました。

「Exception: The number of rows(columns) in the data does not match...」

このエラーは、前述した「サイズ一致の原則」に違反している場合に発生します。直訳すると「データの行数(または列数)と、範囲の行数(または列数)が一致していません」という意味です。

対策: getRange(row, column, numRows, numColumns)の引数に誤りがないか確認してください。特に、配列の中に長さが異なる行が混ざっていないか(例えば、1行目は3列あるのに、2行目は2列しかない等)に注意が必要です。console.log(data)を使って配列の中身を出力し、二次元配列の構造が完全な長方形(全ての行の要素数が同じ)になっているかをデバッグしましょう。

二次元配列になっていないエラー

setValuesは一次元配列(例:[1, "田中"])を直接受け付けることができません。引数には必ず二次元配列(例:[[1, "田中"]])を渡す必要があります。1行だけのデータを書き込む場合であっても、必ず配列を二重(ブラケットを2つ)にして渡すように注意してください。

1行だけでも二重の配列にする

GASを使ったスプレッドシートへのデータ一括書き込みにおいて、setValuesは必須のメソッドです。

  • 実行時間短縮: セル単位のアクセスを排除し、API呼び出しを最小化することでスクリプトの短縮を実現します。
  • サイズ一致の原則: 書き込む二次元配列の縦横サイズと、getRangeで指定する範囲を完全に一致させる必要があります(data.lengthとdata[0].lengthをフル活用しましょう)。

この配列操作と範囲指定の基本を押さえるだけで、あなたのGASコードは後から読みやすくなります。ぜひ実務の自動化スクリプトに一括書き込みを取り入れてみてください。

タイトルとURLをコピーしました