スプレッドシートを、別ファイルへコピーする

GAS
目次

業務でスプレッドシートを運用していると、各現場や担当者が個別に入力した作業用ファイルの中から、完成した特定の月次集計シートだけを取り出して別の一元管理ファイルへ集約したい場面が頻繁に発生します。手作業でシートメニューからコピー先を探して転送することもできますが、締め切り前後に何十ものファイルを行き来して同じ作業を繰り返すと、転送漏れや別名での重複作成といった人為的ミスが避けられません。Google Apps Script(GAS)を使えば、対象のシートオブジェクトを指定してターゲットとなる別のスプレッドシートへ直接シートを丸ごと転送できます。この記事では、GASを使ってシートを別のスプレッドシートへコピーする基本的な書き方から、コピー直後に発生するシート名の仕様、現場運用で突き当たる参照切れや権限エラーの回避方法までをまとめます。

月次集計シートを一元管理ファイルへ集約したい場面

スプレッドシートを別ファイルへコピーしたいという需要は、主に「権限の分離」や「データの集約・配布」を自動化する現場で生まれます。たとえば全社の売上ダッシュボードや経営管理用スプレッドシートは限られた管理者だけが閲覧・編集できるようにしておき、各支社や担当者には自分たちの作業用スプレッドシートだけを配布しているケースです。締め日を過ぎたタイミングで、担当者側の最新確定シートだけを一元管理側の親スプレッドシートへ吸い上げたい場合、ファイル全体の権限を共有することなく、スクリプト側で安全にシートを転送する仕組みが役立ちます。

また、これとは逆のパターンとして、親となるマスターテンプレート用スプレッドシートに用意された定型シートを、新規プロジェクトが立ち上がるたびに個別案件用の別スプレッドシートへ初期データとして配布したい場合にも適しています。毎回手作業でシートをエクスポートしたりインポートしたりしていると、シートの構成変更や書式設定が抜けてしまい、後続の関数や集計ロジックが壊れる原因になります。GASで対象シートを別のスプレッドシートへコピーする処理を組むことで、フォーマットを完全に保ったまま、確実にシート単位のバックアップや配布を完了させることができます。

ただし、単にデータを複製するだけであれば範囲指定して値のみを書き出す方式もあります。それにもかかわらずシートごとコピーするアプローチを選ぶ最大の理由は、列ごとの幅、セルの背景色、条件付き書式、入力規則、セル内の結合状態といったシート固有のレイアウト情報を一切崩さずにそのまま相手先ファイルへ持ち込みたいからです。1セルずつ値や書式を転記するスクリプトを書くと実行時間が膨大になり、6分の実行時間制限に引っかかるリスクが高まりますが、シートのコピー処理であればGoogleの内部処理として高速に丸ごと複製されます。

copyToメソッドで別スプレッドシートへ転送する基本コード

特定のシートを別ファイルへ送るには、転送元となるシートオブジェクトを取得したうえで、copyToメソッドの引数に転送先のスプレッドシートオブジェクトを渡します。以下は、現在開いているスプレッドシートのアクティブなシートを取得し、指定したIDを持つ別スプレッドシートへコピーを追加する最小限のコード例です。

function copyCurrentSheetToAnotherSpreadsheet() {
  // 転送元のシートを取得
  const sourceSheet = SpreadsheetApp.getActiveSheet();
  
  // 転送先のスプレッドシートをID指定で開く
  const targetSpreadsheetId = "TARGET_SPREADSHEET_ID";
  const targetSpreadsheet = SpreadsheetApp.openById(targetSpreadsheetId);
  
  // シートを転送先スプレッドシートへ丸ごと複製
  sourceSheet.copyTo(targetSpreadsheet);
}

このスクリプトを使うタイミングは、担当者が入力作業を終えて「確定」ボタンを押した際や、定期実行トリガーで夜間に最新シートを中央管理ファイルへバックアップしたいときです。SpreadsheetApp.openByIdに渡すIDは、転送先スプレッドシートのURL(https://docs.google.com/spreadsheets/d/◯◯◯/editの◯◯◯部分)から取得します。このIDを固定値としてスクリプト内に持たせるか、スクリプトプロパティや設定シートに逃がしておくことで、特定の受け皿ファイルへ確実にシートを送り込めます。

ここでコードの前後でずれる点として理解しておくべきなのは、copyToメソッドは「シートを転送先に新しく挿入する処理」であり、「既存の同名シートを上書きする処理ではない」という点です。すでに転送先に同じ名前のシートが存在していたとしてもエラーにはならず、新しいシートが末尾に追記されます。そのため、定周期でこのコードをそのまま実行し続けると、転送先ファイルの中にシートが延々と増え続けていくことになります。上書きしたい場合は、事前に同名シートが存在するかチェックして削除するか、後述のようにリネーム処理を組み合わせる必要があります。

コピー後のシート名変更と重複エラーのハンドリング

別スプレッドシートへシートをコピーした際に現場で最も戸惑うのが、コピー先のシート名に関する仕様です。元のシート名が「2024年10月度」だった場合、copyToを実行すると、転送先では自動的に「2024年10月度 のコピー」という名称でシートが生成されます。これはGoogleスプレッドシートの標準仕様であり、同一ファイル内に完全一致するシート名が存在することを防ぐための挙動です。

しかし、転送先のファイルで別のシートやGASが元のシート名「2024年10月度」を参照している場合、「 のコピー」が付与されたままでは参照エラー(#REF!)が発生したり、後続の集計処理で見失ってしまったりします。このズレを回避するためには、copyToの戻り値を利用します。実はcopyToメソッドは、転送先スプレッドシート側に新しく作成された「Sheetオブジェクト」を返します。この戻り値を受け取ることで、コピー完了直後に即座に名称を変更できます。

function copyAndRenameSheet() {
  const sourceSheet = SpreadsheetApp.getActiveSheet();
  const originalName = sourceSheet.getName();
  const targetSpreadsheet = SpreadsheetApp.openById("TARGET_SPREADSHEET_ID");
  
  // 転送先に既存の同名シートがあるか確認し、あれば削除して上書き扱いにする
  const existingSheet = targetSpreadsheet.getSheetByName(originalName);
  if (existingSheet) {
    targetSpreadsheet.deleteSheet(existingSheet);
  }
  
  // コピーを実行し、作成された新しいシートオブジェクトを受け取る
  const newSheet = sourceSheet.copyTo(targetSpreadsheet);
  
  // コピー先で付与された「〜 のコピー」を本来の名前に変更する
  newSheet.setName(originalName);
}

上記のように書くことで、転送先ファイル内ですでに前回の同名シートが存在していた場合は先に削除を行い、その後にコピーして本来の名前へリネームするという、実質的な上書き更新が実現できます。ただし、転送先で既存シートを削除する際は、そのシートが唯一のシートではないことを確認する必要があります。スプレッドシートはシートが0枚になる状態を許可しないため、ファイル内に1枚しかシートがない状態でdeleteSheetを実行するとスクリプトが例外エラーで停止します。その場合は先にcopyToを行ってから古いシートを削除する順序に変更する配慮が求められます。

別ファイルへコピーした後に発生する数式参照と書式のズレ

シートのコピーが正常に完了しても、中身のデータが元の状態と同じように動作するとは限りません。ここで頻繁に現場を悩ませるのが、数式内の参照先や名前付き範囲、保護設定の挙動です。同一ファイル内でのシートコピーとは異なり、別ファイルへの転送ではコンテキストが完全に切り離されるため、いくつかの機能が意図通りに持ち越されません。

第一に、他のシートを参照している数式です。元のファイルで「=マスター!A2」のように別シートのセルを参照していた場合、その「マスター」シートが存在しない別のスプレッドシートへコピーされると、数式は即座にエラーになります。さらに厄介なことに、参照先が存在しない場合でもGoogleスプレッドシートは元のファイルのURLを埋め込んだIMPORTRANGE的な挙動には変換してくれず、単にローカルな参照として解決しようとするため、予期せぬセルを参照してしまう危険があります。これを回避したい場合は、コピーする前に別シート参照数式を値として確定させておくか、マスター情報も含めて関係するシートを一緒に転送する設計にする必要があります。

第二に、シートに設定されている「保護範囲(セル保護)」です。元のシートで特定セルの編集権限を制限していても、別ファイルへcopyToした時点ですべての保護設定は解除され、誰でも編集できる標準セルとして複製されます。もし機密性の高い計算式やロックしておきたい項目がある場合は、コピー完了後にGASを使って転送先シートに対して改めてprotect()メソッドを呼び出し、保護範囲を再定義しなければなりません。

第三に、名前付き範囲(Named Ranges)の競合です。元のシートで名前付き範囲を使用している場合、別ファイルへコピーした際にその定義がコピー先ファイル全体の名前付き範囲として引き継がれます。もし転送先ファイルにすでに同名の名前付き範囲が存在していた場合、名前の後ろに連番が付与されたり、参照範囲が混ざってしまったりして、関数が想定外の値を返す要因になります。別ファイルへのコピーを日常的に行うシートでは、名前付き範囲に過度に依存せず、通常のA1記法やテーブル構造を用いた記述に留めておくのが安全です。

権限やファイル容量によってコピー処理が止まる要因と対策

スクリプトの構文自体は正しくても、実務の運用環境によってcopyToが失敗するケースがあります。最も多いのは実行ユーザーのアクセス権限に起因するエラーです。SpreadsheetApp.openByIdで転送先を開いてシートを書き込むためには、そのスクリプトを実行しているGoogleアカウントが、転送先スプレッドシートに対して「編集者」以上の権限を持っている必要があります。閲覧者権限しか持っていないファイルを指定した場合は、実行時に「アクセス権がありません」という例外が発生してスクリプト全体が停止します。

チームメンバー全員が実行するボタンにスクリプトを割り当てている場合、個々のメンバー全員に転送先ファイルの編集権限を付与しなければ動作しません。しかし、それでは一元管理ファイルの内容をメンバーが手動で勝手に書き換えてしまうリスクが生じます。このジレンマを回避するには、コピー処理をWebアプリケーション(doPost/doGet)として「スクリプトのオーナー」の権限でデプロイし、メンバー側からはAPI経由でシートIDを送信してサーバー側で転送処理を実行させるか、時間主導型トリガーを用いて管理者のアカウントで定期実行させるアーキテクチャを採用するのが有効です。

また、転送先スプレッドシートのセル数上限にも注意が必要です。Googleスプレッドシートには1ファイルあたり1,000万セルという上限が存在します。月次集計シートを毎月・毎週のように別ファイルへ集約し続けていると、不要になった過去のバックアップシートや膨大な空白セルが積み重なり、ある日突然上限に達して新しいシートのコピーを受け付けなくなります。長期運用を行うファイルでは、copyToを実行する定期バッチの中で、一定期間が経過した過去の古いシートを自動アーカイブして別ファイルへ退避させるか、定期的に削除するクリーンアップ処理をあらかじめ組み込んでおくことが安定運用の鉄則です。

同じシート操作の続きは、Googleスプレッドシートの同じスプレッドシート内で、シートを複製する に書いてあります。

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