質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Google フォーム

Google フォームは、 Google社が提供しているアンケートフォーム作成および集計ができる無料のツール。Googleアカウントがあれば利用が可能です。集計データは、スプレッドシートに収集され、データ分析もできます。

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

Q&A

2回答

3142閲覧

GAS 処理速度を速めるためのコツを教えてください

Kazuya_s

総合スコア3

Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Google フォーム

Google フォームは、 Google社が提供しているアンケートフォーム作成および集計ができる無料のツール。Googleアカウントがあれば利用が可能です。集計データは、スプレッドシートに収集され、データ分析もできます。

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

0グッド

0クリップ

投稿2021/05/18 01:47

編集2022/01/12 10:55

GAS初心者です
よろしくお願い致します

teratailさんで質問をしながら進めてまいりました
みなさんのご協力に感謝いたします

今回はGASの処理時間の工夫について教えていただきたく、質問いたします

「フォームの回答 1」というシートがGoogleフォームの回答が反映されているシートで、2500程度の行数と7列のあるデータを扱います

そのシートのデータを1日の終わりに、指定された各スプレッドシートにコピーして、「フォームの回答 1」のシートはデータを削除して翌日を迎える

という流れを作りたいと考えています

現状、下記のスクリプトの記述で、GW後からほぼ毎日動作してくれているのですが、5/16はエラー(Exception:ドキュメントにアクセス中にスプレッドシートのサービスがタイムアウトしました)が出ました

「6分以内に処理を終えないといけない」という問題だと思い、トリガーの設定ページで処理時間の確認をしました

すると、エラーがでた5/16は93.745秒
それ以外の日も565秒〜808秒かかりながらも処理が完了していると表示されていました

トリガーは1日の終わりの23:00〜00:00に発動するように設定しています

6分の壁ということであれば、360秒が限界であり、この処理時間がエラーの原因になっているのだろうと自分としては予想しましたが、解決策を見出すことができませんでした

どうぞよろしくお願い致します

処理の内容としては、回答データは行ごとに個人のデータが回収されているので、行ごとに指定されたスプレッドシートにコピーをしたいです

以下、前回の質問のURLです
https://teratail.com/questions/336191

GAS

1function myFunction() { 2 //コピー元になるデータを取得 3 var ss = SpreadsheetApp.openById('Googleフォームの回答回収スプレッドシート'); 4 var sh = ss.getSheetByName('フォームの回答 1'); 5 var _lastRow = sh.getLastRow(); 6 var data = sh.getRange(2, 1, _lastRow, 7).getValues(); 7 data = sh.getRange("A2:G").getValues(); 8 9 10 var copymasterdata = sh.getRange(2,1,_lastRow,5).getValues(); 11 var copymastersh = ss.getSheetByName('マスターデータ保管'); 12 var _lastRowmasterPlus1 = copymastersh.getLastRow()+1; 13 14 copymastersh.getRange(_lastRowmasterPlus1,1,copymasterdata.length,5).setValues(copymasterdata); 15 16 var classidsh = ss.getSheetByName('IDキー'); 17 var classiddata = classidsh.getDataRange().getValues(); 18 // キーごとにデータを集約するための連想配列 19 var arrays = {}; 20 // キーシートのシートIDをarraysのキーに設定 21 for (var i = 0; i < classiddata.length; i++){ 22 arrays[classiddata[i][0]] = []; 23 } 24 25 // データを各シートIDごとに集約する。 26 for (var i = 0; i < data.length; i++){ 27 line = data[i]; 28 // キーデータのある6列目~7列目を順番に繰り返す。 29 for(var j = 5; j < 7; j++){ 30 // 6列目または7列目のキーがarraysに存在するならば、 31 // そのキーに対応するデータをpush 32 if(line[j] in arrays){ 33 arrays[line[j]].push(line.slice(0,5)); 34 } 35 } 36 } 37 38 // 振り分け先の各スプレッドシート共通のシート名 39 var targetSheetName = 'データ保管'; 40 41 // 集約したデータを各スプレッドシートに書き込む 42 for (var i = 0; i < classiddata.length; i++){ 43 var classid = classiddata[i][0]; 44 var copyss = SpreadsheetApp.openById(classid); 45 var copysh = copyss.getSheetByName(targetSheetName); 46 if (copysh == null){ 47 Logger.log(`エラー:スプレッドシート[ID=${classid}] に「${targetSheetName}」という名前のシートが存在しません。`) 48 return; 49 } 50 var copy_lastRowPlus1 = copysh.getLastRow() + 1; // 書き込み先の最終行+1 51 var copydata = arrays[classid]; // キーを指定して書き込むデータを格納。 52 if (copydata == null || copydata.length < 1) continue; // エラー対策。データがない場合はスキップ。 53 var lastColumn = 5 // カラム数を設定 54 var lastRow = copydata.length; // 行数を取得 55 copysh.getRange(copy_lastRowPlus1, 2, lastRow, lastColumn).setValues(copydata); 56 } 57 Logger.log('処理終了'); 58 59 60//Logger.log(_lastRow); 61 sh.deleteRows(3,_lastRow-2); 62 sh.getRange('A2:G2').clearContent(); 63 64 //二次元配列に埋め込む関数をセット 65 var formformula = [ 66 [ 67 `=arrayformula(vlookup(B2:B,'名簿'!A:J,10,0))`, 68 `=arrayformula(vlookup(B2:B,'名簿'!A:K,11,0))` 69 ] 70 ]; 71 72 //関数を埋め込む 73 sh.getRange(2,6,1,2).setValues(formformula); 74}

以下、ダミーファイルによるスプレッドシートの構成です

フォームの回答 1

マスターデータ保管

名簿

IDキー
これは「IDキー」のシートで、タイトルなどはなく、検索したいスプレッドシートキーのみを入力しています

データ保管
写真では「データ保管用」となっていますが、実際には「データ保管」という名前のシートを使用しています

以下、2021/5/18 21:06 追記

振りイメージ説明レッドシートに含まれているシートのダミー画像です
全部で5つのシートで構成されています
それぞれのシートに関数が組み込まれているので、画像に内容を書き込みました

  1. データ保管
  2. today
  3. 項目1
  4. 項目3
  5. query

today
todayシート

項目1
項目1シート

項目3
項目3シート

query
queryシート

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/05/18 02:17 編集

前の御質問の追記で 「F列とG列には、別シートからarrayformula(vlookup)という関数が2行目に入っており、回答がされていくと同時に所属するシートIDが割り当てられるようにしてあります」 「F列はAグループから、G列はBグループからIDを検索した方がスピードアップできそうなイメージはあったのですが、どうして良いのかわからなかったので、AグループとBグループのIDをまとめてA列に並べて、A列1列のみのIDシート(classidsh)を現在は利用しています。」 ここの部分ですが、文章だけでは正直、正確なイメージが分かりにくいです。(どのシートのG列?Aグループとは?Bグループとは?) 前にもコメントしましたが、単純に「1シート内のベタ貼り付けのデータ」を別のシートに振り分けコピーするのであれば、 私の環境(CPUが特別早いわけでもない特に普通のノートパソコンです。ネットも早くないです)で2500件を100グループ以上に分けるのも、70秒くらいで処理を終えられていました。 質問者さんの環境で時間がかかる原因は、単純な1シート内のベタで貼り付けられたデータの処理ではなく、「各シートを連携し、シート元(またはシート先)に数式を入れている」ということがあるのだと思われます。 つまり、貼付処理ごとに数式計算処理が走っているために時間がかかっている可能性があります。 しかしながら、全体がどういう状態であるのか示していただかないことには、再現できず解析できないため、解決のしようがないと思います。 したがって、可能であれば下記を示していただきたく思います。 ・関係するシートの画面キャプチャ及び各シートの意味(そのシートは何をするためのシートかという説明) ・シート内に入っている数式のキャプチャ(及びその数式の意味・目的) ・もし質問のスクリプトが一部でも省略したものであるならば、省略していない実際のスクリプト(省略した部分がバグや遅延の要因になっていることもあるため) 個人情報の問題でそのまま載せられないならば、ダミーの適当なデータを使用し、「同じようなデータがあと2500行続きます」という説明で構いません。 知りたいのは、スクリプトで処理しようとしている各シート・関係する数式の場所とその数式の具体的な内容、すなわちまとめると"問題となっている処理の、正確かつ具体的な全体像"ということです。
Kazuya_s

2021/05/18 03:27

qnoirさん、いつもありがとうございます 現在ダミーファイルを用意していますが、名簿ファイルにもvlookup関数が複数埋め込まれており、ご指摘の通り処理速度を遅くしていた可能性を見つけられました いったん、不要なvlookupを削除し、値として貼り付けてみました そしてダミーファイルを用意します
退会済みユーザー

退会済みユーザー

2021/05/18 03:37

お手数おかけします。 前の御質問で、私の回答中に、テスト時に使っていた余計な1行が入っていたので、削除しました。 (7行目の「data = sh.getRange("A2:G").getValues();」です。)すみません。
退会済みユーザー

退会済みユーザー

2021/05/18 04:49 編集

振り分け先(貼り付け先)の各スプレッドシートには数式が全く入っていないという理解でよろしいでしょうか? →質問文より、A列に数式が入っていることは確認しました。 (追加質問)各振り分け先のスプレッドシートには「データ保管」シート1枚しか存在せず、同じスプレッドシート内の他のシートや、他のスプレッドシートから数式で参照されてはいない、という理解でよろしいでしょうか。
Kazuya_s

2021/05/18 10:01

振り分け先のスプレッドシートには「データ保管」とその他に4つのシートがあります その他の4つのシートは「データ保管」シートから関数でデータを拾うことになっています
退会済みユーザー

退会済みユーザー

2021/05/18 10:17

具体的にどのような関数・範囲でしょうか? 貼り付け先が他のシートから計算式(関数)で参照されている場合、貼り付けるたびに再計算が発生するため、遅くなると思います。
Kazuya_s

2021/05/18 11:42

度々、ありがとうございます 振り分け先のスプレッドシートのダミーファイルを作りにくいので、まず文面で説明させていただきます 1. 所属メンバーの名簿があり、importrangeとvlookupでGoogleフォームと連動している「フォームの回答 1」シートからデータを検索しています 2. 縦軸に名簿、横軸に日付(5/18のようなm/d形式)。同一スプレッドシート内の「データ保管」シートのA列にある検索値(日付&メールアドレス)からvlookupで「項目1」のデータを検索しています 3. 2.と同様のシートで、読み出す内容が異なるシートで「項目3」を検索しています 4. query関数を用いて、「データ保管」シートの中で「項目2」に入力があったデータを検索しています 以上の構成となっております したがいまして、3つのシートが「データ保管」シートを参照・検索しています
macaron_xxx

2021/05/18 23:53

とりあえずログに処理時間を吐き出して、ボトルネックがどこかを探しましょう。
guest

回答2

0

みなさん短くするご提案なので、
別のご提案としてgoolge Workspaceにすると制限が伸びて30分になります。
どうにも時間内で収まらなさそうであれば、有料版も検討の価値ありです。700円くらいなので。

処理が制限時間ギリギリということであれば、
各シートへ貼り付ける前のデータを一時的にどこかのシート保存しておき、
時間をずらしたトリガーで貼り付けという方法もありです。

投稿2021/05/19 08:15

Tatsunosuke

総合スコア599

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Kazuya_s

2021/05/19 08:19 編集

Tatsunosukeさん、お返事ありがとうございます 現在、Google Workspace for educationの登録となっています そうなると、6分の壁が関係ないから800秒くらいのスクリプトが走って完了まで動き続けてくれているということなのでしょうか
Tatsunosuke

2021/05/19 08:44

すみません、ちょっとお待ちください。制限が、6分になってます。 ちょっと確認いたします。
Tatsunosuke

2021/05/19 08:51

全てのアカウントが6分に変更されたようです。 大変失礼しました。 となると、やはり処理を分けるのがよろしいかと思います。
Tatsunosuke

2021/05/19 08:56

ただ、僕の方もいくつか6分を超える処理が走るものがあるのですが、今のところトラブルの連絡が上がってこないので暫定処置として制限の6分を過ぎても問題ないのかも知れません。
Kazuya_s

2021/05/19 09:08

なるほど やはり無料版なので、いずれかの形で6分の壁を越えなくてはならないのですね 私の場合、作業を3つか4つに分ければ良いということですよね
Tatsunosuke

2021/05/20 03:53

時間かがかかるのは、SSへのアクセス(主に読み出し、書き込み)なので、ここが分割できれば問題ないと思います。 もし自分がやるとしたら、 ◼️データを集計用の関数 ①各シートに貼り付けるデータをまとめる。 ②一時保存としてssに保存 終了 ◼️貼り付け用の関数 2時間毎に起動するようにトリガーを設定。 ①開始時間取得 ②一時保存データを取得 データが空でなければ↓ ③各シートへ貼り付ける ④途中で時間が5分を経過したら、 一時保存のデータを一時保存用のシートに保存する。 貼り付けが完了した配列は削除。 一時保存用のシートに値がある限りは、貼り付ける用の関数が処理し続けるので、24h/2時間*5分=60分は回せます。 確かgasの一日制限も60分だったような。 万が一これでも処理しきれないと言うことになると、そもそものシステムの設計を見直す必要があります。
guest

0

同じようなシート構成・数式構成で、2500行、7列、100個のクラス分けで
トリガーを設置して何回かやってみましたが、いずれも手元では2-3分以内で終了しました。

データの貼り付け先シートが複数のシートから数式で参照されているとのことだったため
再計算により遅くなるかもしれないと思ったのですが、
現状シートを開かない限り再計算されないように思われるため、
スクリプト上での単純な貼り付けであればそこまで処理速度に影響はないようです。

質問文によれば「90秒でタイムアウトになることもあれば、6分以上(565秒〜808秒)かかっても終わることもある」とのことで、謎です。(特に90秒でタイムアウトの件)

環境要因があるのかもしれません。

6分以上の時間がかかる前提で、
Google Apps Scriptで6分の壁(タイムアウト)を突破する
を参考に、タイムアウト時間内で処理を分割するという手法が適用できるかもしれませんが、
私の力では実装できませんでした。

なお、前回では記載が無かった「貼り付け先の『データ保管』シートのA列に数式が入っている」という点に関してですが
この状態で前のスクリプトを実行すると、既存データの続きではなく、表示画面の最後尾にデータが書き込まれてしまうと思いました。(自分の環境ではそうなった)
(=arrayformura(b2:b~)が設定されているシートに、getLastRow()関数を適用した場合、データのある最終行ではなく、ワークシートの最大範囲の最終行が返ってくるため)

したがって、下記のように修正した方がよいかもしれません。
(これ自体は処理スピードに関係ないですが・・・)

diff

1function myFunction2() { 2 //コピー元になるデータを取得 3 var ss = SpreadsheetApp.openById('Googleフォームの回答回収スプレッドシート'); 4 var sh = ss.getSheetByName('フォームの回答 1'); 5 var _lastRow = sh.getLastRow(); 6 var data = sh.getRange(2, 1, _lastRow, 7).getValues(); 7- data = sh.getRange("A2:G").getValues(); // テスト用に誤って書いていたコードのため削除 8 9 var copymasterdata = sh.getRange(2,1,_lastRow,5).getValues(); 10 var copymastersh = ss.getSheetByName('マスターデータ保管'); 11 var _lastRowmasterPlus1 = copymastersh.getLastRow()+1; 12 13 copymastersh.getRange(_lastRowmasterPlus1,1,copymasterdata.length,5).setValues(copymasterdata); 14 15 var classidsh = ss.getSheetByName('IDキー'); 16 var classiddata = classidsh.getDataRange().getValues(); 17 // キーごとにデータを集約するための連想配列 18 var arrays = {}; 19 // キーシートのシートIDをarraysのキーに設定 20 for (var i = 0; i < classiddata.length; i++){ 21 arrays[classiddata[i][0]] = []; 22 } 23 24 // データを各シートIDごとに集約する。 25 for (var i = 0; i < data.length; i++){ 26 line = data[i]; 27 // キーデータのある6列目~7列目を順番に繰り返す。 28 for(var j = 5; j < 7; j++){ 29 // 6列目または7列目のキーがarraysに存在するならば、 30 // そのキーに対応するデータをpush 31 if(line[j] in arrays){ 32 arrays[line[j]].push(line.slice(0,5)); 33 } 34 } 35 } 36 37 // 振り分け先の各スプレッドシート共通のシート名 38 var targetSheetName = 'データ保管'; 39 40 // 集約したデータを各スプレッドシートに書き込む 41 for (var i = 0; i < classiddata.length; i++) { 42 var classid = classiddata[i][0]; 43 if (classid == null || classid == 0) continue; 44 var copyss = SpreadsheetApp.openById(classid); 45 var copysh = copyss.getSheetByName(targetSheetName); 46 if (copysh == null) { 47 Logger.log(`エラー:スプレッドシート[ID=${classid}] に「${targetSheetName}」という名前のシートが存在しません。`); 48 continue; 49 } 50 // キーを指定して書き込むデータをcopyataに格納。 51- var copy_lastRowPlus1 = copysh.getLastRow() + 1; // 削除 52// ↓のようにしないと、既存データの続きにデータが書き込まれないと思われます。 53+ var copy_lastRowPlus1 = copysh.getRange("B:B").getValues().filter(String).length + 1; // 書き込み先の最終行+1 54 55 var copydata = arrays[classid]; // キーを指定して書き込むデータを格納。 56 if (copydata == null || copydata.length < 1) continue; // エラー対策。データがない場合はスキップ。 57 var lastColumn = 5; // カラム数を設定 58 var lastRow = copydata.length; // 行数を取得 59 60 copysh.getRange(copy_lastRowPlus1, 2, lastRow, lastColumn).setValues(copydata); 61 62 } 63 Logger.log("処理終了"); 64 65 //Logger.log(_lastRow); 66 sh.deleteRows(3,_lastRow-2); 67 sh.getRange('A2:G2').clearContent(); 68 69 //二次元配列に埋め込む関数をセット 70 var formformula = [ 71 [ 72 `=arrayformula(vlookup(B2:B,'名簿'!A:J,10,0))`, 73 `=arrayformula(vlookup(B2:B,'名簿'!A:K,11,0))` 74 ] 75 ]; 76 77 //関数を埋め込む 78 sh.getRange(2,6,1,2).setValues(formformula); 79}

(以上、根本的な解決とはならず申し訳ございません。)

投稿2021/05/18 13:03

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Kazuya_s

2021/05/18 13:11

色々と実験までしてくださり、いつもありがとうございます。 貼り付け先の「データ保管」シートは、これまでの蓄積があります 日付ごとにこぴーをしているので、5/18の昼時点では5/17までのデータがあり、5/18の23:00-0:00に振り分けコピーがされます 5/17のデータがすでにあるため、getLastRow()に問題はありません エラーのデータが伸びてしまうこともありましたが、余計な行を削除してしまうことでなぜか解決し、新しいデータを貼り付けても無駄なエラー行が作成されることがなくなりました このような状況ですが、書き加えていただいたfilterスクリプトを入れた方が安心な感じになりますか?
退会済みユーザー

退会済みユーザー

2021/05/18 13:16

蓄積があるということですね。現状で問題がないなら特に変えなくても大丈夫だと思います。
Kazuya_s

2021/05/19 09:55

なるほど ありがとうございます
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問