脚本需要11到20秒才能查找18,000行数据集中的项目

时间:2017-10-31 17:21:24

标签: hash google-apps-script google-sheets google-sheets-api

我有两张Google表格工作簿。

一个是"主人"具有基于制造商项目#的密钥的查找数据源,其可以是从1234到A-01/234-Name_1的任何内容。此工作表通过SpreadsheetApp.openByUrl引用,具有18,000行和13列。键列已转换为纯文本,工作表按此列排序。

第二个是"模板"人们输入他们需要查看主人的项目#s,一次通常20-1500个项目。

脚本在模板中。这是非常缓慢的,经常在30分钟后超时。它是由其他人编写的,我是App Script的新手,但我想我已经设法理解了脚本正在做什么以及瓶颈在哪里发生。

它有很多东西,但这是查找的内容:

var numrows = master.getDataRange().getNumRows();
var masterdata = master.getDataRange().getValues();
var itemnumberlist = template.getDataRange().getValues();
var retreiveddata = [];     
// iterate through the manf item number list to find all matches in the   
// master and return those matches to another sheet
 for (i = 1; i < template.getDataRange().getValues().length; i++) {
   for (j = 0; j < numrows; j++) {
     if (masterdata[j][1].toString() === itemnumberlist[i][1].toString()) { 
      retreiveddata.push(data[j]);
       anothersheet.appendRow(data[j]); 
     } 
   }
 } 

我使用Logger.log()来确定每次通过i循环需要11到19秒,这看起来很疯狂。

我一直在做谷歌搜索,我尝试了几件不同的事情......

首先,我尝试将已发现数据的写入移出for循环,因此脚本将首先执行所有读取,然后写入一个大块,但我无法完全正确地完成它。我的两次尝试都在下面。

var mycounter = 0;
for (i = 0; i < template.getDataRange().getValues().length; i++) {
   for (j = 0; j < numrows; j++) {
     if (masterdata[j][0].toString() === itemnumberlist[i][0].toString()) { 
      retreiveddata.push(masterdata[j]);
      mycounter = mycounter + 1;
     } 
   }
 }

// Attempt 1 
// var myrange = retreiveddata.length;   
//  for(k = 0; k < myrange; k++) {
//    anothersheet.appendRow(retreiveddata.pop([k]);  
//    }

//Attempt 2
 var myotherrange = anothersheet.getRange(2,1,myothercounter, 13)  
 myotherrange.setValues(retreiveddata);  

我无法记得,因为这是在周五,但我认为这两次尝试都导致脚本试图将整个主文件写入&#34; anothersheet&#34;。

所以我暂时把它放在一边,决定尝试其他的东西。我试图在几个示例电子表格中重新创建该问题,但我无法这样做。同样的脚本正在通过我的15,000行示例&#34; master&#34;每次查找不到1秒的文件。我唯一能想到的是我使用随机数作为我的键而不是一个奇怪的文本字符串。

这让我想到,也许我可以在主数据和要查找的值上使用哈希算法,但这提出了另外一组问题。

我从另一个论坛帖子中借用了这些功能:

function GetMD5Hash(value) {
  var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, 
value);
  var txtHash = '';
    for (j = 0; j <rawHash.length; j++) {
   var hashVal = rawHash[j];
    if (hashVal < 0)
      hashVal += 256; 
    if (hashVal.toString(16).length == 1)
      txtHash += "0";
    txtHash += hashVal.toString(16);
     Utilities.sleep(100);
  }
    return txtHash;
}

function RangeGetMD5Hash(input) {
  if (input.map) {            // Test whether input is an array.
    return input.map(GetMD5Hash); // Recurse over array if so.
    Utilities.sleep(100);
  } else {
    return GetMD5Hash(input)
  }
}

我花了一整天的时间来获取主电子表格中所有18,000个项目#的哈希值。 GetMD5Hash和RangeGetMD5Hash都不会一致地返回值。我一次只能做几行。有时我得到#34;正在加载......&#34;无限期。有时我会得到#34;#Name&#34;有关GetMD5Hash未定义的消息(尽管它在前一行上工作)。有时我得到##;;错误&#34;有关内部错误的消息。

此方法实际上将每个项目的查找时间减少到2-3秒(更好,但不是很好)。但是,我无法使用哈希函数来一致地处理输入数据。

此时我非常沮丧并且在我的其他工作上落后于我认为我会在这些论坛上与聪明人接触并希望得到某种奇迹般的回应。

总结一下,我正在寻找有关这三项的建议:

  1. 在尝试将写入移出for循环时,我做错了什么?
  2. 有没有办法更快地获取哈希值或使用不同的方法来实现相同的目标?
  3. 我还可以尝试哪些方法来加快脚本速度?
  4. 非常感谢您提供的任何建议!

    -Mandy

1 个答案:

答案 0 :(得分:0)

听起来你尝试将appendRow()调用移出循环,就会遇到正确的方法。无论何时您正在阅读或写入电子表格,您都可以预期个人通话需要1到2秒,因此当您获得匹配时,这将耗费大量时间。将匹配存储在数组中并立即将它们全部写出来是可行的方法。

我注意到的另一件事是你的脚本在实际的for循环条件语句中调用getValues()。每次循环迭代时都会执行条件语句,因此即使您没有匹配也可能浪费大量时间。

最终调整可能会有所帮助,具体取决于您所希望的行为。您可以在找到第一个匹配后停止内部for循环,如果您只关心第一个匹配或知道只有一个匹配,则会为您节省大量迭代。要做到这一点,请放置&#34; break&#34;紧接在retreiveddata.push(masterdata[j]);行之后。

要解决getValues问题,请更改:

for (i = 1; i < template.getDataRange().getValues().length; i++) {

要:

for (i = 1; i < itemnumberlist.length; i++) {

然后修复了appendRow问题,包括中断调用:

for (i = 1; i < itemnumberlist.length; i++) {
   for (j = 0; j < numrows; j++) {
     if (masterdata[j][0].toString() === itemnumberlist[i][0].toString()) { 
      retreiveddata.push(masterdata[j]);
      break; //stop searching after first match, move on to next item
     } 
   }
 }

 //make sure you have data to write before trying to write it.
 if(retreiveddata.length > 0){  
    var myotherrange = anothersheet.getRange(2,1,retreiveddata.length, retreiveddata[0].length);
    myotherrange.setValues(retreiveddata);  
 }

如果您正在重复使用相同的表格,那么#34; anothersheet&#34;在每次执行时,您可能还需要在编写新结果之前调用anothersheet.clear()来删除任何现有数据。

我会完全传递哈希方法,比较字符串是比较字符串,所以无论它们是哈希值还是实际的部件号,我都不会期望有显着差异。