【问题标题】:Merging many spreadsheets into report file exceeds maximum execution time将许多电子表格合并到报告文件中超过了最大执行时间
【发布时间】:2018-11-06 02:08:26
【问题描述】:

如果学分小于 x,我正在使用以下脚本在 Google 电子表格中添加来自学生循环的文件行。该脚本运行良好,但由于每天都在添加电子表格中的数据,现在脚本抛出“超过最大执行时间”错误(我们有超过 2000 个文件)。由于我是脚本新手,我不知道如何优化代码。

有人可以帮我优化代码或任何解决方案,以使执行时间少于 5 分钟。每次与电子邮件进行比较时,都必须将其与许多电子邮件进行比较。请帮忙!

function updated() {  
  //Final file data (Combined)
  var filecombined = SpreadsheetApp.openById("XXXXXXXXXX");
  var sheet2 = filecombined.getSheets();

  //Folder with all the files 
  var parentFolder = DriveApp.getFolderById("YYYYYYYYYYYY");
  var files = parentFolder.getFiles();

  //Current Date
  var fecha = new Date();

  //Path for each file in the folder
  while (files.hasNext()) {
    var idarchivo = files.next().getId();
    var sps = SpreadsheetApp.openById(idarchivo);

    var sheet = sps.getSheetByName('STUDENT PROFILE');
    var data = sheet.getDataRange().getValues();
    var credits = data[5][1];

    //Flat; bandera:1 (new row), bandera:2 (update row)
    var bandera = 1;

    //Take data from final file (Combined) 
    var data2 = sheet2[0].getDataRange().getValues();

    //If credits are less than X: write
    if (credits < 120) {
      var email = data[2][1];
      var lastrow = filecombined.getLastRow();
      var u = 0;
      //comparison loop by email, if found it, update and exit the loop
      while (u < lastrow) {
        u = u + 1;
        if (email == data2[u - 1][1]) {
          sheet2[0].getRange(u, 3).setValue(credits);
          sheet2[0].getRange(u, 4).setValue(fecha);
          u = lastrow;
          bandera = 2;
        }
      }
      //if that email does not exist, write a new row
      if (bandera == 1) {
        var nombre = data[0][1];
        sheet2[0].getRange(lastrow + 1, 1).setValue(nombre);
        sheet2[0].getRange(lastrow + 1, 2).setValue(email);
        sheet2[0].getRange(lastrow + 1, 3).setValue(credits);
        sheet2[0].getRange(lastrow + 1, 4).setValue(fecha);
      }
    }
  }
  SpreadsheetApp.flush();
}

【问题讨论】:

  • 如果您使用SpreadsheetApp.getActive().getSheetByName("STUDENT PROFILE").hideSheet(); 隐藏工作表,它是否会运行得更快?
  • 您是否正在显示所有 2000 个文件?也许你也可以隐藏它们。这样屏幕就不必继续更新,最终结果将是相同的。不过,对此不确定。
  • 不要在while (u &lt; lastrow)循环中调用setValue,而是将这些数据存储到一个二维数组中并在循环外调用setValues
  • 您需要阅读电子邮件一次,并创建一个支持比数组遍历更快的查找的数据结构。例如。查看我的解决方案 hereherehere。另请阅读此问题:stackoverflow.com/questions/18706085/…

标签: optimization google-apps-script google-sheets


【解决方案1】:

提问者的代码运行时间超过 4-6 分钟,并且出现错误 Exceeded maximum execution time

以下答案仅基于提问者提供的代码。我们没有关于“文件组合”电子表格、其大小和触发器的任何信息。我们也对各种学生电子表格、它们的大小等一无所知,但我们知道这些文件有 2,000 个。我们不知道这个例程运行的频率,也不知道有多少学生有学分

getvaluessetvalues 语句非常昂贵;通常每个 0.2 秒。提问者代码包括各种这样的陈述 - 有些是不可避免的,但有些则不是。

在优化此代码时,我做了两个主要更改。
1 - 我移动了第 27 行 var data2 = sheet2[0].getDataRange().getValues();
这一行只需要执行一次,我将它重新定位在代码的顶部,就在各种“filecombined”命令之后。就目前而言,这条线为每个学生电子表格执行一次;这可能导致了几分钟的执行时间。

2) 我将某些setvalue 命令转换为数组,然后在处理结束时仅从数组更新“文件组合”电子表格一次。根据学分低且尚未在“文件组合”表上的学生数量,这可能意味着大量节省。 受影响的代码是第 47 到 50 行。

line47:         sheet2[0].getRange(lastrow+1, 1).setValue(nombre);
line48:         sheet2[0].getRange(lastrow+1, 2).setValue(email);
line49:         sheet2[0].getRange(lastrow+1, 3).setValue(credits);
line50:         sheet2[0].getRange(lastrow+1, 4).setValue(fecha);

在第 38 行和第 39 行也执行了 setvalue 命令(如果学生已经在“文件组合”电子表格中),但我选择保留这些原样。如上所述,我们不知道可能有多少这样的学生,这些setvalue 命令的成本可能很小也可能很小。在这一点明确之前,并且考虑到其他时间节省,我选择保持原样。


function updated() {  

  //Final file data (Combined)
  var filecombined = SpreadsheetApp.openById("XXXXXXXXXX");
  var sheet2 = filecombined.getSheets();

    //Take data from final file (Combined) 
    var data2 = sheet2[0].getDataRange().getValues();

    // create some arrays
    var Newdataarray = [];
    var Masterarray = [];

  //Folder with all the files 
  var parentFolder = DriveApp.getFolderById("YYYYYYYYYYYY");
  var files = parentFolder.getFiles();

  //Current Date
  var fecha = new Date();

  //Path for each file in the folder
  while (files.hasNext()) {
  var idarchivo = files.next().getId();
  var sps = SpreadsheetApp.openById(idarchivo);

  var sheet = sps.getSheetByName('STUDENT PROFILE');
  var data = sheet.getDataRange().getValues();
  var credits = data[5][1];

  //Flat; bandera:1 (new row), bandera:2 (update row)
  var bandera = 1;

    //If credits are less than X: write
    if (credits < 120){
        var email = data[2][1];
        var lastrow = filecombined.getLastRow();
        var u = 0;
        //comparison loop by email, if found it, update and exit the loop
        while (u < lastrow) {
         u = u + 1;
         if (email == data2[u-1][1]){
           sheet2[0].getRange(u, 3).setValue(credits);
           sheet2[0].getRange(u, 4).setValue(fecha);
           u = lastrow;
           bandera = 2;
         }
        }
        //if that email does not exist, write a new row
        if(bandera == 1){
         var nombre = data[0][1];
          Newdataarray = [];
          Newdataarray.push(nombre);
          Newdataarray.push(email);
          Newdataarray.push(credits);
          Newdataarray.push(fecha);
          Masterarray.push(Newdataarray);
        }
    }
  }
  // update the target sheet with the contents of the array
  // these are all adding new rows
  lastrow = filecombined.getLastRow();
  sheet2[0].getRange(lastrow+1, 1, Masterarray.length, 4);
  sheet2[0].setValues(Masterarray); 

  SpreadsheetApp.flush();
    }

【讨论】:

    【解决方案2】:

    正如我在my comment 中提到的,您遇到的最大问题是您反复在数组中搜索一个值,而您可以使用much faster 查找函数。

    // Create an object that maps an email address to the (last) array
    // index of that email in the `data2` array.
    const knownEmails = data2.reduce(function (acc, row, index) {
      var email = row[1]; // email is the 2nd element of the inner array (Column B on a spreadsheet)
      acc[email] = index;
      return acc;
    }, {});
    

    然后您可以通过尝试获取电子邮件的值来确定data2 中是否存在电子邮件:

    // Get this email's index in `data2`:
    var index = knownEmails[email];
    if (index === undefined) {
      // This is a new email we didn't know about before
      ...
    } else {
      // This is an email we knew about already.
      var u = ++index; // Convert the array index into a worksheet row (assumes `data2` is from a range that started at Row 1)
      ...
    }
    

    要了解我们如何从data2 构建knownEmails,您可能会发现Array#reduce 上的文档很有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-21
      • 1970-01-01
      • 1970-01-01
      • 2016-05-07
      相关资源
      最近更新 更多