[英]Google Script - exceeded maximum execution time
我建立了一个脚本,该脚本从多个电子表格中提取并计算总值。 基本上,发生的事情是多个员工将输入为特定客户执行特定任务的时间。 然后,我想在主电子表格上计算为特定客户完成了多少工作,并进行了一些分析。
因此,在主电子表格上,每个客户都有一个选项卡。 此功能从主电子表格中获取工作表名称,并创建一个包含所有客户名称的数组。 我这样做的原因是,如果创建了新选项卡,则该客户名称将自动包含在客户数组中。
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());
};
};
};
下一个功能将查看特定Google Drive文件夹中的所有文件,并以数组形式返回ID。 这使我可以创建存储在此特定文件夹中的员工电子表格的副本,并且该电子表格的值将在更改时自动进行计算。
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;
};
最后一个功能是大大降低了它的速度。
首先,我为所有可能的服务创建一个数组。 不幸的是,这里有137个独立服务。
然后我遍历所有客户。 对于每位客户,我都会遍历每项服务,以查看其是否出现在任何员工电子表格中。 我认为有一种更有效的方法可以做到这一点。 Google脚本在我获得一位完整客户之前就超时了。 另外,我什至没有包括所有员工的电子表格。 我只是在测试使用伪数据。
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;
};
};
};
};
};
第一张图片显示了员工电子表格的外观。 第二个显示单个客户表的外观。 主电子表格上有许多客户表。
您会注意到的一件事是,每个服务都有一个类别。 我当时想也许要检查类别,然后再检查特定的服务。 有23个类别。
我还希望有一种方法可以只查看实际上已经对它们完成工作的服务。 如果没有人进行过“广告系列设置”,则可能会被忽略。
任何帮助将不胜感激。 我为冗长的帖子表示歉意!
您正在循环内多次调用服务。 这些每次最多可能需要几秒钟,通常被认为非常慢。 这违反了Apps Script的最佳做法,因为它大大增加了您的执行时间。
TL; DR:您可能要调用数千次Apps Script服务。 每次通话最多可能需要几秒钟才能执行。 您需要将服务调用缓存在循环之外,否则您的性能将受到严重影响。
例子 :
1:
if (sheets[i].getName() != "Services" && sheets[i].getName() != "Employee Files")
创建并设置一次具有工作表名称的变量,然后检查该变量,而不是两次调用getName()
方法。 这不是什么大问题,但是会增加执行时间。
2:
这是一个大问题,因为它在您的循环中calculateNumbers
var lastColumn = servicesSpreadsheet.getSheetByName(currentCustomer).getLastColumn(); var servicesRange = SpreadsheetApp.openById("1X3RRR3UVeot-DYCyXOsfVo0DoKjHezltwBPwUm8ZYig").getSheetByName(currentCustomer).getRange("A4:BC227").getValues();
2A:您正在打开一个新的电子表格,打开一个新的工作表,然后得到一个范围在同一张纸上,每环获得该范围内的值,当您的servicesRange
。 这些服务调用将迅速堆叠,并会使您的执行时间膨胀。
2b:我看到您正在获取lastColumn
,但是我在任何地方都看不到它吗? 也许我错过了一些东西,但是在为每个循环进行服务调用时未使用它会增加您的执行时间。
3:
这是巨大的,您可能在这里成千上万次调用Apps Script服务。 此代码段已经深了两个循环级别。
//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;
};
};
};
您在调用相同服务的嵌套循环中具有多级嵌套循环。 如果您有一个三次嵌套的循环,每个循环被迭代10次,而底层则每个循环调用一次服务。 您将调用一次Apps Script服务1,000次。 保守地说,假设每个服务调用为0.5秒, 那么您已经有8.3分钟的执行时间。
缓存您的服务呼叫,并执行批量操作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.