2016-07-29 31 views
2

Ich habe ein Skript erstellt, das aus mehreren Tabellen zieht und Gesamtwerte berechnet. Grundsätzlich passiert, dass mehrere Mitarbeiter Stunden eingeben, die für eine bestimmte Aufgabe für einen bestimmten Kunden durchgeführt wurden. Ich möchte dann berechnen, wie viel Arbeit für einen bestimmten Kunden in einer Haupttabelle ausgeführt wurde, und einige Analysen durchführen.Google Script - maximale Ausführungszeit überschritten

Also in der Master-Tabelle, gibt es eine Registerkarte für jeden Kunden. Diese Funktion übernimmt die Blattnamen aus der Haupttabelle und erstellt ein Array aller Kundennamen. Der Grund, warum ich dies tue, ist so, dass wenn ein neuer Tab erstellt wird, dieser Kundenname automatisch in das Kunden-Array aufgenommen wird.

function getCurrentCustomers() { 
    var sheets = servicesSpreadsheet.getSheets(); 
    for (var i=0 ; i < sheets.length ; i++) { 
    if (sheets[i].getName() != "Services" && sheets[i].getName() != "Employee Files") { 
     currentCustomers.push(sheets[i].getName()); 
    }; 
    }; 
}; 

Die nächste Funktion wirft einen Blick auf alle Dateien in einem bestimmten Ordner Google Drive und gibt die IDs in einem Array. Dadurch kann ich eine Kopie einer Mitarbeiter-Tabelle erstellen, die in diesem bestimmten Ordner gespeichert ist. Die Werte dieser Tabelle werden automatisch berechnet, wenn sie sich ändern.

function listFilesInFolder() { 
    var folder = DriveApp.getFolderById("0B7zrWIHovJrKVXlsaGx0d2NFT2c"); 
    var contents = folder.getFiles(); 

    var cnt = 0; 
    var file; 

    while (contents.hasNext()) { 
    //Finds the file in the specified folder 
    var file = contents.next(); 
    //Increases the count 
    cnt++; 
    //Gets the Id of the file 
    data = [ 
     file.getName(), 
     file.getId() 
     ]; 
    //Appends it to the employeeId list 
    employeeIds.push(data); 
    }; 
    return employeeIds; 
}; 

Die letzte Funktion ist die, die es sehr verlangsamt.

Zuerst erstelle ich ein Array für alle möglichen Dienste. Leider gibt es einzelne Dienste.

Dann durchlaufen ich alle Kunden. Für jeden Kunden durchlaufe ich jeden Dienst, um zu sehen, ob er auf den Tabellen eines Mitarbeiters erscheint. Ich denke, es gibt einen effizienteren Weg, dies zu tun. Das Google-Skript läuft ab, bevor ich einen vollständigen Kunden durchgehe. Außerdem habe ich noch nicht einmal die Tabellen für alle Angestellten hinzugefügt. Ich teste gerade mit Dummy-Daten.

function calculateNumbers(){ 
    var allServices = servicesSpreadsheet.getSheetByName("Services").getRange("Z2:Z137").getValues(); 
    Logger.log(allServices); 
    Logger.log(allServices[0][0]); 
    employeeList = listFilesInFolder(); 

    //Gets services spreadsheet range 


    /*Loops through all of the current customers (currentCustomers comes from function getCurrentCustomers)*/ 
    for (var c = 0; c < currentCustomers.length; c++) { 

    var currentCustomer = currentCustomers[c]; 
    var lastColumn = servicesSpreadsheet.getSheetByName(currentCustomer).getLastColumn(); 
    var servicesRange = SpreadsheetApp.openById("1X3RRR3UVeot-DYCyXOsfVo0DoKjHezltwBPwUm8ZYig").getSheetByName(currentCustomer).getRange("A4:BC227").getValues(); 

    //Loops through all of the services 
    var serviceTotal = 0;  
    for (var service = 0; service < allServices.length; service++){ 

     //Loops through employee spreadsheet Ids 
     for (var i = 0; i < employeeList.length; i++) { 
     //Get employee spreadsheet ID 
     var spreadsheetId = employeeList[i][1]; 

     //Open the employee spreadsheet by ID 
     var employeeSpreadsheet = SpreadsheetApp.openById(spreadsheetId); 

     //Get the sheets from the particular employee spreadsheet 
     var sheets = employeeSpreadsheet.getSheets(); 

     //Gets the name of each sheet in the employee spreadsheet 
     var sheetsName = []; 
     for (var j = 0; j < sheets.length; j++) { 
      sheetsName.push(sheets[j].getName()); 
     }; 

     //Loops through all of the sheets in an employee spreadsheet ignoring the Services spreadsheet 
     for (var q = 0; q < sheetsName.length; q++) { 
      if (sheetsName[q] != "Services") { 

      //Gets the range of the spreadsheet 
      var range = employeeSpreadsheet.getSheetByName(sheetsName[q]).getRange("A5:E1000").getValues(); 

      //Loops through the range to see if range matches service and customer 
      for (var r = 0; r < range.length; r++) { 
       if (range[r][3] == allServices[service][0] && range[r][1] == currentCustomer) { 
       serviceTotal += range[r][4]; 
       }; 
      }; 
      }; 
     };   
     }; 

     //Adds the service total to the correct customer's spreadsheet 
     for (var serviceServices = 4; serviceServices <= servicesRange.length; serviceServices++){ 
     var end = 0; 
     if (end > 0) {break} 
     else if (allServices[service][0] == servicesSpreadsheet.getSheetByName(currentCustomer).getRange(serviceServices,1).getValues()) {   
      servicesSpreadsheet.getSheetByName(currentCustomer).getRange(serviceServices,6).setValue(serviceTotal); 
      end += 1; 
     }; 
     }; 
    }; 

    }; 
}; 

This is what an Employee Spreadsheet Looks Like

This is part of a customer's sheet on the master spreadsheet

Das erste Bild zeigt, was Mitarbeiter Tabelle aussieht. Die zweite zeigt, wie ein einzelnes Kundenblatt aussieht. Es gibt viele Kundenblätter in der Master-Tabelle.

Eines der Dinge, die Sie bemerken werden, ist, dass jeder Dienst eine Kategorie hat. Ich überlegte, ob ich vielleicht die Kategorie überprüfen und dann den jeweiligen Service überprüfen sollte. Es gibt 23 Kategorien.

Ich hoffte auch, dass es einen Weg gab, nur auf Dienste zu schauen, die tatsächlich Arbeit an ihnen hatten. Wenn noch niemand ein Kampagnen-Setup durchgeführt hat, könnte das vielleicht ignoriert werden.

Jede Hilfe würde sehr geschätzt werden. Ich entschuldige mich für den langen Post!

Antwort

4

Sie rufen Dienste innerhalb von Schleifen mehrmals auf. Diese können bis zu ein paar Sekunden dauern und werden im Allgemeinen als sehr langsam angesehen. Dies ist gegen Apps Script best practice, da es Ihre Ausführungszeit erheblich erhöht.

TL; DR: Sie rufen Apps Script Services möglicherweise tausende Male an. Jeder Anruf kann bis zu ein paar Sekunden dauern. Sie müssen Ihre Service-Anrufe außerhalb Ihrer Schleifen zwischenspeichern, sonst wird Ihre Leistung fürchterlich leiden.

Beispiele:

1:

if (sheets[i].getName() != "Services" && sheets[i].getName() != "Employee Files") 

erstellen und eine Variable mit dem Blattnamen einmal gesetzt, und prüfen Sie, ob statt zweimal die getName() Methode aufzurufen. Dies ist keine große Sache, wird aber die Ausführungszeit erhöhen.

2:

Dies ist ein großes Problem, da es in calculateNumbers

var lastColumn = servicesSpreadsheet.getSheetByName(currentCustomer).getLastColumn(); 
var servicesRange = SpreadsheetApp.openById("1X3RRR3UVeot-DYCyXOsfVo0DoKjHezltwBPwUm8ZYig").getSheetByName(currentCustomer).getRange("A4:BC227").getValues(); 

2a in der Schleife eine Ebene tiefer ist: Sie öffnen eine neue Tabelle, Öffnen eines neuen Arbeitsblatts und dann Abrufen eines Bereichs für dasselbe Arbeitsblatt und Abrufen der Werte dieses Bereichs einmal pro Schleife für Ihre servicesRange. Diese Serviceaufrufe werden schnell stapeln und Ihre Ausführungszeit aufblähen.

2b: Ich sehe, Sie werden immer die lastColumn, aber ich sehe es nicht überall eingesetzt? Vielleicht habe ich etwas verpasst, aber es wird nicht benutzt, während ein Service-Aufruf für jede Schleife noch mehr zu Ihrer Ausführungszeit beiträgt.

3:

Dies ist massiv, werden Sie möglicherweise Apps hier Dienste Tausende oder Zehntausende von Zeiten Script aufrufen. Dieses Snippet ist bereits zwei Schleifenebenen tief.

//Loops through all of the sheets in an employee spreadsheet ignoring the Services spreadsheet 
     for (var q = 0; q < sheetsName.length; q++) { 
      if (sheetsName[q] != "Services") { 

      //Gets the range of the spreadsheet 
      var range = employeeSpreadsheet.getSheetByName(sheetsName[q]).getRange("A5:E1000").getValues(); 

      //Loops through the range to see if range matches service and customer 
      for (var r = 0; r < range.length; r++) { 
       if (range[r][3] == allServices[service][0] && range[r][1] == currentCustomer) { 
       serviceTotal += range[r][4]; 
       }; 
      }; 
      }; 
     };   
     }; 

     //Adds the service total to the correct customer's spreadsheet 
     for (var serviceServices = 4; serviceServices <= servicesRange.length; serviceServices++){ 
     var end = 0; 
     if (end > 0) {break} 
     else if (allServices[service][0] == servicesSpreadsheet.getSheetByName(currentCustomer).getRange(serviceServices,1).getValues()) {   
      servicesSpreadsheet.getSheetByName(currentCustomer).getRange(serviceServices,6).setValue(serviceTotal); 
      end += 1; 
     }; 
     }; 
    }; 

Sie haben mehrstufige verschachtelte Schleifen in verschachtelten Schleifen, die dieselben Dienste aufrufen. Wenn Sie eine dreimal geschachtelte Schleife hatten, wobei jede Ebene zehnmal wiederholt wurde, und die unterste Ebene einen Dienst einmal pro Schleife aufruft. Sie würden 1.000 Mal einen Apps Script-Dienst aufrufen. Wenn Sie etwa 0,5 Sekunden pro Serviceanruf, , konservativ sind, hätten Sie bereits 8,3 Minuten Ausführungszeit.

Zwischenspeichern Sie Ihre Serviceanrufe und führen Sie Massenvorgänge durch.

+0

Wow! Das ist absolut erstaunlich. Ich kann Ihnen nicht genug danken, dass Sie sich die Zeit genommen haben, all diese Fragen zu beantworten. Ich werde die Änderungen vornehmen und sehen, was die Laufzeit ist. (Offensichtlich) Ich bin sehr neu in der Codierung. Das ist das Komplizierteste, was ich gebaut habe, und es kam unglaublich ineffizient, aber du hast mir geholfen zu verstehen, warum. Wenn ich dir einen Drink kaufen könnte, würde ich es in einer Sekunde tun. Danke @DouglasGaskell! Ehrlich gesagt, es war so lange her, dass ich meistens erwartete, dass niemand antwortete, aber ich dachte, ich würde es versuchen. – tonestrike

Verwandte Themen