可能由于getValue和单元格插入而导致处理时间过长

时间:2016-02-09 10:09:06

标签: javascript optimization google-apps-script google-sheets google-spreadsheet-api

我刚刚编写了我的第一个谷歌应用程序脚本,从VBA移植,格式化了一列客户订单信息(感谢您的所有指示)。

说明:

代码通过 - 前缀标识状态代码,然后将以下名字与姓氏(如果存在)组合在一起。然后写下"订单完成"姓氏本来就是哪里。最后,如果订单之间没有间隙,它会插入一个必要的空白单元格(见下图)。

问题:

问题是处理时间。它无法处理更长的数据列。我被警告说,脚本大量使用了Method Range.getValue。

现有优化:

根据对this question的回复,我试图在循环外尽可能多地保留变量,并且还改进了我的if语句。 @MuhammadGelbana建议只调用一次Range.getValue方法,并以其值来移动......但我不明白这将如何/可行。

代码:

function format() {

var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var lastRow = s.getRange("A:A").getLastRow();
var row, range1, cellValue, dash, offset1, offset2, offset3;

  //loop through all cells in column A
  for (row = 0; row < lastRow; row++) {
    range1 = s.getRange(row + 1, 1);

    //if cell substring is number, skip it
    //because substring cannot process numbers
    cellValue = range1.getValue();
    if (typeof cellValue === 'number') {continue;};
    dash = cellValue.substring(0, 1);

    offset1 = range1.offset(1, 0).getValue();
    offset2 = range1.offset(2, 0).getValue();
    offset3 = range1.offset(3, 0).getValue();

    //if -, then merge offset cells 1 and 2
    //and enter "Order complete" in offset cell 2.
    if (dash === "-") {
       range1.offset(1, 0).setValue(offset1 + " " + offset2);
       //Translate
       range1.offset(2, 0).setValue("Order complete");
     };

    //The real slow part...
    //if - and offset 3 is not blank, then INSERT CELL
    if (dash === "-" && offset3) {
       //select from three rows down to last
       //move selection one more row down (down 4 rows total)
       s.getRange(row + 1, 1, lastRow).offset(3, 0).moveTo(range1.offset(4, 0));
     };    
  };
}

Screenshot example

格式化更新:

有关使用字体或背景颜色格式化输出的指导,请查看此后续问题here。希望你能从这些专业人士给我的建议中受益:)

2 个答案:

答案 0 :(得分:4)

使用.getValue().moveTo()等方法在执行时可能非常昂贵。另一种方法是使用batch operation获取所有列值,并在一次调用中写入工作表之前根据需要迭代数据整形。运行脚本时,您可能已注意到以下警告:

  

该脚本使用的方法很昂贵。每   调用会生成对远程服务器的耗时调用。那   可能会对脚本的执行时间产生严重影响,   特别是大数据。如果性能是脚本的问题,   你应该考虑使用另一种方法,例如Range.getValues()。

使用.getValues().setValues()您的脚本可以重写为:

function format() {

  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var s = ss.getActiveSheet();
  var lastRow = s.getLastRow(); // more efficient way to get last row
  var row;

  var data = s.getRange("A:A").getValues(); // gets a [][] of all values in the column
  var output = []; // we are going to build a [][] to output result

  //loop through all cells in column A
  for (row = 0; row < lastRow; row++) {
    var cellValue = data[row][0];
    var dash = false;
    if (typeof cellValue === 'string') {
      dash = cellValue.substring(0, 1); 
    } else { // if a number copy to our output array
      output.push([cellValue]); 
    }
    // if a dash  
    if (dash === "-") {
      var name = (data[(row+1)][0]+" "+data[(row+2)][0]).trim(); // build name
      output.push([cellValue]); // add row -state
      output.push([name]); // add row name 
      output.push(["Order complete"]); // row order complete
      output.push([""]); // add blank row
      row++; // jump an extra row to speed things up
    } 
  }
  s.clear(); // clear all existing data on sheet
  // if you need other data in sheet then could
  // s.deleteColumn(1);
  // s.insertColumns(1);

  // set the values we've made in our output [][] array
  s.getRange(1, 1, output.length).setValues(output);
}

使用20行数据测试脚本显示执行需要4.415秒,上述代码在0.019秒内完成

答案 1 :(得分:2)

问题:

  • 在循环中使用$cimSession = New-CimSession -ComputerName "dfs.MYDOMAIN" New-DfsnRoot -Path "\\MYDOMAIN\DfsnRoot" -TargetPath "\\dfs.MYDOMAIN\DfsnRoot" -Type DomainV2 -EnableSiteCosting $true New-DfsnFolder -Path "\\MYDOMAIN\DfsnRoot\ShareName" -State Online -TargetPath "\\FILESERVER\ShareName" -ReferralPriorityClass globalhigh -CimSession $cimSession .getValue()会导致处理时间增加。

文档摘录:

  • 最小化对服务的呼叫:

      

    您可以在Google Apps脚本本身中完成的所有操作都比进行需要从Google服务器或外部服务器获取数据的调用(例如对电子表格,文档,站点,翻译,UrlFetch等的请求)要快得多。

  • 高级缓存:

      

    Google Apps脚本已经具有一些内置的优化功能,例如使用超前缓存来检索脚本可能获得的内容,并编写缓存来保存可能设置的内容。

  • 最小化“读写”次数:
      

    您可以编写脚本以通过最大程度地减少读取和写入次数来最大程度地利用内置缓存。

  • 避免交替读/写:
      

    替换读写命令很慢

  • 使用数组:
      

    要加速脚本运行,请使用一个命令将所有数据读入数组,对数组中的数据执行任何操作,然后使用一个命令将数据写出。

慢脚本示例:

.setValue()
  • 请注意,每个循环进行两次调用。有两个循环。在此示例中,对于2x4数组的简单复制粘贴,进行了8个读调用和8个写调用。
  • 此外,请注意,读写调用交替进行,使“超前”缓存无效。
  • 服务呼叫总数:16
  • 耗时:〜5 +秒

快速脚本示例:

/** 
 * Really Slow script example
 * Get values from A1:D2
 * Set values to A3:D4
 */

function slowScriptLikeVBA(){
  const ss = SpreadsheetApp.getActive();
  const sh = ss.getActiveSheet();
  //get A1:D2 and set it 2 rows down
  for(var row = 1; row <= 2; row++){
    for(var col = 1; col <= 4; col++){
      var sourceCellRange = sh.getRange(row, col, 1, 1);
      var targetCellRange = sh.getRange(row + 2, col, 1, 1);
      var sourceCellValue = sourceCellRange.getValue();//1 read call per loop
      targetCellRange.setValue(sourceCellValue);//1 write call per loop
    }
  }
}
  • 服务呼叫总数:2
  • 耗时:〜0.2秒

参考文献: