为啥我的 Apps 脚本删除循环运行如此缓慢?我可以提高性能吗?

Posted

技术标签:

【中文标题】为啥我的 Apps 脚本删除循环运行如此缓慢?我可以提高性能吗?【英文标题】:Why is my Apps Script deletion loop running so slowly? Can I improve performance?为什么我的 Apps 脚本删除循环运行如此缓慢?我可以提高性能吗? 【发布时间】:2019-05-31 13:12:19 【问题描述】:

我的公司每天通过 Google 表单回复收集制造数据几十次。现在,我将引用的工作表接近 10,000 行。最近,我对一些 Google Apps 脚本代码(来自其他 SO 线程和论坛)进行了 frankensteined,以从超过 7 天的大型电子表格中删除行。在试验期间(在工作表的副本上),脚本每秒删除 3-4 行。现在,代码每 7-8 秒只删除一行。我不知道为什么我的代码运行如此缓慢。我的循环有问题吗?

我在开发过程中在其他几张表格上尝试过这段代码,效果非常好。当脚本首次应用于目标工作表时,它按预期运行。然而,当我几天前再次运行它时,脚本执行得非常慢。

删除行的功能由在浏览器中打开工作表后加载的自定义菜单选项触发。

function onOpen() 
  SpreadsheetApp.getUi()
                .createMenu('Delete')
                .addItem('Delete Rows Older than 7 Days', 'deleteOldEntries')
                .addToUi();


function deleteOldEntries() 
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName("Form Responses 1");    //name of the sheet
  //var sheet = ss.getActiveSheet();
  var datarange = sheet.getDataRange();
  var lastrow = datarange.getLastRow();
  var values = datarange.getValues();   //get all data in a 2D array

  var currentDate = new Date();   //today
  var weekOld = Date.now() + -7*24*3600*1000;   //for dates longer than 7 days, change only the 7 to a higher number
  for (i=lastrow;i>=2;i--) 
    var tempDate = values[i-1][0];  //2D arrays are organized as [row][column]; arrays are 0 indexed so row1 = values[0] and col12 = [11]
    if ((tempDate!="") && (tempDate <= (weekOld)))
    
      sheet.deleteRow(i);
    
  

我预计性能至少为每秒几行,但实际性能已降至每分钟 6-7 行。该脚本在 30 分钟时超时,这是 Google 预设的时间限制(如果我没记错的话)。

提前致谢!

编辑:这是上次运行代码的部分执行记录(整个执行记录很长)。今天早上进行的最后一次运行的几乎所有条目似乎都刚刚超过 4 秒。

[19-05-31 08:57:24:875 EDT] Sheet.deleteRow([5681]) [4.048 seconds]
[19-05-31 08:57:29:295 EDT] Sheet.deleteRow([5680]) [4.419 seconds]
[19-05-31 08:57:34:013 EDT] Sheet.deleteRow([5679]) [4.718 seconds]
[19-05-31 08:57:38:561 EDT] Sheet.deleteRow([5678]) [4.547 seconds]
[19-05-31 08:57:42:991 EDT] Sheet.deleteRow([5677]) [4.43 seconds]
[19-05-31 08:57:47:599 EDT] Sheet.deleteRow([5676]) [4.607 seconds]
[19-05-31 08:57:52:122 EDT] Sheet.deleteRow([5675]) [4.522 seconds]
[19-05-31 08:57:56:683 EDT] Sheet.deleteRow([5674]) [4.56 seconds]
[19-05-31 08:58:00:950 EDT] Sheet.deleteRow([5673]) [4.266 seconds]
[19-05-31 08:58:05:450 EDT] Sheet.deleteRow([5672]) [4.5 seconds]
[19-05-31 08:58:10:349 EDT] Sheet.deleteRow([5671]) [4.898 seconds]
[19-05-31 08:58:14:736 EDT] Sheet.deleteRow([5670]) [4.387 seconds]
[19-05-31 08:58:19:697 EDT] Sheet.deleteRow([5669]) [4.96 seconds]
[19-05-31 08:58:28:412 EDT] Sheet.deleteRow([5668]) [8.714 seconds]
[19-05-31 08:58:33:601 EDT] Sheet.deleteRow([5667]) [5.189 seconds]
[19-05-31 08:58:38:845 EDT] Sheet.deleteRow([5666]) [5.243 seconds]
[19-05-31 08:58:43:313 EDT] Sheet.deleteRow([5665]) [4.468 seconds]
[19-05-31 08:58:47:964 EDT] Sheet.deleteRow([5664]) [4.65 seconds]
[19-05-31 08:58:52:947 EDT] Sheet.deleteRow([5663]) [4.982 seconds]
[19-05-31 08:58:58:172 EDT] Sheet.deleteRow([5662]) [5.223 seconds]
[19-05-31 08:59:03:036 EDT] Sheet.deleteRow([5661]) [4.864 seconds]
[19-05-31 08:59:07:957 EDT] Sheet.deleteRow([5660]) [4.92 seconds]
[19-05-31 08:59:12:727 EDT] Sheet.deleteRow([5659]) [4.769 seconds]
[19-05-31 08:59:12:800 EDT] Execution failed: Exceeded maximum execution time [1,803.124 seconds total runtime]

【问题讨论】:

您能否在问题中包含您的执行记录,以便我们查看哪些步骤耗时最长?这可以在运行脚本后转到View &gt; Execution transcript 找到。 当然。我马上添加。 【参考方案1】:

您需要使用批处理操作。

片段:

  var thisBatch = 0;//added
  for (i=lastrow;i>=2;i--) 
    var tempDate = values[i-1][0];  
    if ((tempDate!="") && (tempDate <= (weekOld)))
    
        ++thisBatch;//increment this batch
     else if(thisBatch) // previous batch ended
        sheet.deleteRows(i+1, thisBatch); //delete next row and all of that batch 
        thisBatch = 0;
    
  
 thisBatch ? sheet.deleteRows(i+1, thisBatch) : null;//to remove any batch after remaining after end of loop

如果仍然很慢,您需要使用高级 Google 服务来访问工作表 api 并发出 Delete Dimension Request。还可以通过手动删除工作表底部的所有空行来修剪工作表。

【讨论】:

这只会替换我目前拥有的整个for 循环,对吗?我将立即对此进行测试。感谢您提供我尚未遇到的建议。 @Maestro 是的 + 顶部和底部的一些补充 效果非常好,一次删除了 5,500 多行。感谢您的帮助! @TheMaster 这个脚本很棒,只有一个问题。使用(thisBatch &amp;&amp; values[i]) ? 而不是thisBatch ? 的目的是什么 - 前者是否也检查values[i] 是否不为空? @ross 这是最后一分钟的补丁,可能不需要。我有点担心,如果i+1 超出了工作表的范围,这将使values[i] 未定义,从而阻止 sheet.deleteRows 执行/抛出错误。再想一想,情况可能并非如此,因为循环设计为从 lastRow-1 而非 lastrow 本身运行。

以上是关于为啥我的 Apps 脚本删除循环运行如此缓慢?我可以提高性能吗?的主要内容,如果未能解决你的问题,请参考以下文章

从 Access 中的另一个表返回值时,为啥我的查询运行如此缓慢?

为啥我的所见即所得编辑器在 vuejs 中运行如此缓慢?

为啥 Docker 构建命令在 Elastic Beanstalk 中运行如此缓慢?

为啥我使用多处理/多线程的函数在 for 循环中使用时如此缓慢,但在循环之外却没有?

为啥在 FROM 子句中再添加一个 INNER JOIN 会使我的 SQL 查询如此缓慢?

为啥附加到调试器的运行如此缓慢?