2014-11-27 8 views
4

Первого раза оприходования, долгое время чтения :)Google Apps Script Перебор Таблиц очень медленно

Я только что написал свой первый Скрипт Google Apps для сопоставления информации из 14 таблиц, каждый лист с 2-30 рабочим листами в отдельная таблица отчетов.

Сценарий выполняется красиво, он проверяет один столбец для данных и, если он найден, захватывает имя электронной таблицы, имя рабочего листа, данные первого столбца для строки и данные из столбца проверки и добавляет его в массив в качестве суб- массив данных.

Затем он вычисляет область массива подматриц и записывает данные в файл отчета (из которого выполняется сценарий).

Моя единственная проблема заключается в том, что для запуска сценария требуется около 2 минут.

Интересно, был ли я неэффективен в своем методе и надеялся, что кто-то сможет просмотреть сценарий и сообщить мне, допустил ли я некоторые ошибки?

здесь идет:

/** 
* Retrieves all the rows in the active spreadsheet that contain data and logs the 
* values for each row. 
* For more information on using the Spreadsheet API, see 
* https://developers.google.com/apps-script/service_spreadsheet 
*/ 

function getFaults() { 
/** opens each spreadsheet for Liddon and examines the "Report/Replace" column "F" 
if there is data there then get the 
[Sheetname], [fault area (column "A" row relative to the "F" field found)] and the ["F" field data] 
**/ 
var reportsheet = SpreadsheetApp.getActiveSheet(); 
var reportdata = [] 
var reportrow = 0 

var liddonblocks = [ 
"1APshQevK7iZxhP7--zmtuM3K6dPTgTZjmNarQ6CEsV4", "1riCQMOa38jo4nCD4qjW1BFZKk5xpXFZiCXHzXpiYKIU", "1NTKXmted1-U12MiqvCGRuYBdhPy1_eLiPn7v8_oVKFE", "1RKOJUNNi5TAg5dETZDtLjZOkUSheuguzmtdPelMclMI", 
"1b5-fzCp0wzW8llpUc_6xi1iTFzsapZh9ASSFgDYt4WU", "1qJtY37K0zwoJcz7LdyHhWgkypRMP9LabBchNLM4Fgow", "1yvf4W8-SkfTH-n-PdDNQeyEDEz-shzTe-Id57S_YB2M", "1ETZc1xeNGXU6ipb1XQiD8SiIyRXzZtiJfS4AClKroJk", 
"1tJ5u3Hv0uz-n2cdw-QYixKnuMG9skvrUbz1UROhIm34", "1DjhmIdD0GrPxR-fv7pCPkIwIyfai5BHsK9GhT-Hcs3k", "15w39NZZIacD1OfiTWG1E3HmOhV0B_e2Jsuan_ySwf2Q" , "1cK2HBLEftYOZEkCcxs1TX1PxcJRiKTZpQrcsOfE4B1s", 
"16W_bfMKk98wkLpEmm2Q68Ta_SrCA8EBarQyGF2yfm18","1_Z_tgF5UAfq3fxPsDEe40z2GZSehhL-u4hEuVszrbn8" ] 

// loop through the spreadsheets 
for (block = 0; block < liddonblocks.length; block++) { 
    //open the spreadsheet using the index from the liddonblocks list 
    var ss = SpreadsheetApp.openById(liddonblocks[block]); 
    //get all of the sheets within the spreadsheet 
    var sheets = ss.getSheets(); 

//loop through each sheet in each spreadsheet using the length of the number of sheets in the  spreadsheet as the index 
for (var sheetnum = 0; sheetnum < sheets.length; sheetnum++) { 
    //get an array of all data in the sheet 
    //assigns array in the form of: [[area, fault], [Bedroom, Broken Bed], [Bathroom, ]] 
    //where each sub-array is a row of data starting at row 1 eg: [[row1-col1, row1-col2...],[row2-col1, row2-col2...]...] 
    data = sheets[sheetnum].getDataRange().getValues(); 
    //get the text name of the sheet 
    name = sheets[sheetnum].getSheetName(); 

    // iterate over the data set and look for values in the 5th column, starting at row 7 to exclude the headers. 
    // this is the column named "Report/Replace " 
    for (var count = 7; count < data.length; count++) { 
    if (data[count][5] != "" && data[count][5] != 0) { 
     //if there is data in the 5th column of the row then append the following data to the reportdata array and a sub-array 
     // [ sheetname, columnA, columnF ] 
     reportdata[reportrow] = [ ss.getName(), name, data[count][0], data[count][5]] 
     //increment the reportcount variable so any further hits on data in column 5 are created as sequentail sub-arrays in the reportdata array. 
     reportrow++ 
    } 
    } 
} 
} 
//write the contents of reportdata to the console 
var range = reportsheet.getRange(2,1,reportrow,reportdata[0].length); 
range.setValues(reportdata); 
} 

/** 
* Adds a custom menu to the active spreadsheet, containing a single menu item 
* for invoking the readRows() function specified above. 
* The onOpen() function, when defined, is automatically invoked whenever the 
* spreadsheet is opened. 
* For more information on using the Spreadsheet API, see 
* https://developers.google.com/apps-script/service_spreadsheet 
*/ 
function onOpen() { 
    var spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); 
    var entries = [{ 
    name : "Update Report", 
    functionName : "getFaults" 
}]; 
spreadsheet.addMenu("Keble Scripts", entries); 
}; 
+2

IMHO этот код уже оптимизирован ... Google-Apps-Script не самый быстрый, что бы вы ни делали. btw, 2 минуты является разумным в отношении количества листов. –

+1

Nit-picky: 'if (data [count] [5]! =" "&& data [count] [5]! = 0)' можно заменить на 'if (data [count] [5])', так как '' '' и '0' являются« ложными »([Спецификация языка ECMAScript] (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf), раздел 9.2) , Это позволит исключить одно сравнение за цикл и обложку для значений «null» ... но вряд ли будет заметным по сравнению с вызовами службы GAS. – Mogsdad

+0

Спасибо Mogsdad - иногда я теряюсь в логическом состоянии, я обрезаю это! :) –

ответ

3

Я согласен с комментарием Serge, что этот код уже хорошо оптимизирован, открытие, что много таблиц собираются занять некоторое время.

Я вижу одну возможность улучшить, но она, вероятно, будет иметь очень минимальное влияние на скорость, если таковая имеется. Вы можете переместить вызов ss.getName() из внутреннего цикла, вместо этого назначьте его переменной сразу после открытия электронной таблицы, а затем ссылайтесь на эту переменную во внутреннем большинстве циклов.

Обратите внимание, что, по моему опыту, скорость вызовов службы Google имеет тенденцию сильно варьироваться, поэтому иногда это может происходить быстрее или медленнее. Вы можете видеть, сколько времени занимает каждый вызов, просмотрев в Execution Transcript редактора сценариев в меню «Вид».

+0

Я думал, что перемещение вызова getName за пределы цикла данных будет на самом деле имеют существенное значение. – AdamL

+1

Возможно, вы правы AdamL, я, возможно, слишком сильно хеджировал в своих формулировках. Я не уверен, что Google делает любое кэширование вызовов функций, подобных этому за кулисами. Возможно, последующие вызовы ss.getName() быстрее первого, поэтому я не хотел предполагать, что изменение будет иметь большое влияние, но это очень хорошо. –

+0

Спасибо Кэмерон, я получу тестирование и отчитаю назад - я не знал о транскрипции исполнения, поэтому записывать различия! –

4

Прежде всего, просмотрите информацию на this page.

Две вещи, которые я испытал, что действительно может замедлить вас вниз являются:

  1. Вызов внешней службы несколько раз
  2. Вызов SpreadsheetApp.flush() в цикле, чтобы заставить обновления листа.
  3. Вызов Range.getValue() много.

Для первой части попробуйте выполнить дозвон. Например, если вы запрашиваете обменные курсы для своей местной и иностранной валюты, попробуйте отправить несколько запросов в один запрос.

Для второй части просто не называйте этот метод, если это действительно необходимо.

Для третьей части попробуйте изменить код, чтобы один раз вызвать этот метод и переместить его результат, а не вызвать его несколькими способами для одного и того же результата.

Подсказка. Чтобы избежать изменения многих параметров метода, просто передайте один объект в начале вызова метода-дерева и заполните его параметрами. Таким образом, вам не нужно изменять каждый метод, который вы передаете, чтобы добавить \ удалить параметр. Это концепция, применяемая ко всем языкам программирования с функциями.

+0

Спасибо Мухаммеду, отличные советы! –