将许多电子表格合并到报告文件中超过了最大执行时间

时间:2018-10-25 17:17:41

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

如果学分少于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();
}

2 个答案:

答案 0 :(得分:2)

发问者的代码需要4到6分钟才能运行,并且收到错误Exceeded maximum execution time

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

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

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

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();
    }

答案 1 :(得分:0)

正如我在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)
  ...
}

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