问题
I'm importing a range from another spreadsheet and pasting it in the current spreadsheet with this script:
function getdata() {
var values = SpreadsheetApp.openById('XXXXXX').
getSheetByName('SheetB').getRange('A4:AZ484').getValues();
var destsheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var destrange = destsheet.getRange('B520:BA1000');
destrange.setValues(values);
}
When firing the script manually the execution lasts around 20 seconds and has no problems at all:
[19-09-06 23:34:48:847 PDT] Starting execution
[19-09-06 23:34:48:999 PDT] SpreadsheetApp.openById([XXXXXX]) [0.145 seconds]
[19-09-06 23:34:49:000 PDT] Spreadsheet.getSheetByName([SheetB]) [0 seconds]
[19-09-06 23:34:49:000 PDT] Sheet.getRange([A4:AZ484]) [0 seconds]
[19-09-06 23:35:07:353 PDT] Range.getValues() [18.352 seconds]
[19-09-06 23:35:07:361 PDT] SpreadsheetApp.getActive() [0 seconds]
[19-09-06 23:35:07:510 PDT] Spreadsheet.getSheetByName([Sheet1]) [0.148 seconds]
[19-09-06 23:35:07:511 PDT] Sheet.getRange([B520:BA1000]) [0 seconds]
[19-09-06 23:35:09:907 PDT] Range.setValues([[[Tue Jan 01 00:00:00 GMT+01:00 2019, 235.1840336134454, 9.0, 41.525999999999996, 10.0, 21.0, 13.0, 13.0, 8.0, 59.43, 0.0, 0.0, 0.0, 0.0, 166.36, 0.0, 269.14, 8.0, 33.6425, 246.7, 1.0909606809890555,...) [2.374 seconds]
[19-09-06 23:35:10:571 PDT] Execution succeeded [21.055 seconds total runtime]
However, when being time-driven triggered (this Script should run automatically once per day) the execution time goes up to 374.518 secs and gets timed out.
Edited-----> I've been splitting the script and logging it:
function getdata() {
var ss = SpreadsheetApp.openById('XXXXX');console.log('s1');
var sh =ss.getSheetByName('SheetB');console.log('s2');
var si = sh.getRange('A4:AZ484');console.log('s3');
var sj = si.getValues(); console.log('step1')
var destsheet = SpreadsheetApp.getActive().getSheetByName('Sheet1'); console.log('step2')
var destrange = destsheet.getRange('B520:BA1000'); console.log('step3')
destrange.setValues(sj); console.log('step4')
}
Apparently it gets stuck in "var sj = si.getValues(); console.log('step1')".
The whole range is aroung 25.000 cells, which I think it shouldn't be too much.
Anyone has an idea how could this be happening?
Thank you very much for help!
回答1:
TL;DR The fastest approach to this issue is at UPDATE II.
ORIGINAL ANSWER: getValues & setValues
We tried to reproduce this error by creating a spreadsheet six times larger than yours (150,000 cells in total). Then we run a function to copy it to a blank spreadsheet using your approach. After many runs, we got an average of 43 seconds to complete. This is the exact function we used to replicate your operation:
function copySheet(){
var ss1 = SpreadsheetApp.openById('{ORIGINAL SPREADSHEET}').getSheetByName('Sheet1');
var ss2 = SpreadsheetApp.openById('{COPY SPREADSHEET}').getSheetByName('Sheet1');
var originalData = ss1.getRange(1, 1, 75000, 2).getValues();
var targetRange = ss2.getRange(1, 1, 75000, 2).setValues(originalData);
}
And this is an example of a real execution transcript:
[19-09-09 04:09:27:021 PDT] Starting execution
[19-09-09 04:09:27:122 PDT] SpreadsheetApp.openById([{ORIGINAL SPREADSHEET}]) [0.093 seconds]
[19-09-09 04:09:27:123 PDT] Spreadsheet.getSheetByName([Sheet1]) [0 seconds]
[19-09-09 04:09:27:211 PDT] SpreadsheetApp.openById([{COPY SPREADSHEET}]) [0.086 seconds]
[19-09-09 04:09:27:211 PDT] Spreadsheet.getSheetByName([Sheet1]) [0 seconds]
[19-09-09 04:09:27:212 PDT] Sheet.getRange([1, 1, 75000, 2]) [0 seconds]
[19-09-09 04:09:50:166 PDT] Range.getValues() [22.953 seconds]
[19-09-09 04:09:50:396 PDT] Sheet.getRange([1, 1, 75000, 2]) [0.001 seconds]
[19-09-09 04:10:06:199 PDT] Range.setValues() [15.59 seconds]
[19-09-09 04:10:07:128 PDT] Execution succeeded [39.172 seconds total runtime]
When we run this function using a trigger we got similar time values. With all this information we can deduce that the size of the range is not an issue. Since your function stop working in the getValues()
line, and as you said it is a spreadsheet with a lot of information and linked to several sources, we recommend to repeat this operation in a replicated spreadsheet with zero links to workaround the problem. Alternatively, you could provide us with an anonymized spreadsheet to test it. We hope that this is useful for you. Please, don't hesitate to offer us more information for further help.
UPDATE I: copyTo, getValues & setValues
To copy just the data itself (nor the links nor the formulas) we can use the method copyTo
with the following parameters. Unfortunately, that method can only be used to copy data inside the same spreadsheet. As a workaround, the following code will create a new sheet inside the original spreadsheet, will later copy just the data to that new sheet and after that will copy that sheet to the destination spreadsheet. Finally, the intermediate sheet will be deleted.
function copySheet2() {
var ss1 = SpreadsheetApp.openById('{ORIGINAL SPREADSHEET}');
var copySheet = ss1.insertSheet('Clone');
var ss2 = SpreadsheetApp.openById('{COPY SPREADSHEET}').getSheetByName('Sheet1');
var originalData = ss1.getSheetByName('Sheet1').getRange(1, 1, 75000, 2);
var destinationRange = ss1.getSheetByName('Clone').getRange(1, 1, 75000, 2);
originalData.copyTo(destinationRange, {
contentsOnly: "true"
});
var cloneData = ss1.getSheetByName('Clone').getRange(1, 1, 75000, 2).getValues();
var destinationSpreadsheet = ss2.getRange(1, 1, 75000, 2).setValues(cloneData);
ss1.deleteSheet(ss1.getSheetByName('Clone'));
}
As we can see on the new execution transcript, with this approach we can vastly reduce the execution time.
[19-09-11 10:11:50:333 CEST] Starting execution
[19-09-11 10:11:50:391 CEST] SpreadsheetApp.openById([{ORIGINAL SPREADSHEET}]) [0.052 seconds]
[19-09-11 10:11:52:132 CEST] Spreadsheet.insertSheet([Clone]) [1.741 seconds]
[19-09-11 10:11:54:012 CEST] SpreadsheetApp.openById([{COPY SPREADSHEET}]) [1.879 seconds]
[19-09-11 10:11:54:012 CEST] Spreadsheet.getSheetByName([Sheet1]) [0 seconds]
[19-09-11 10:11:57:376 CEST] Spreadsheet.getSheetByName([Sheet1]) [3.364 seconds]
[19-09-11 10:11:57:377 CEST] Sheet.getRange([1, 1, 75000, 2]) [0 seconds]
[19-09-11 10:11:57:377 CEST] Spreadsheet.getSheetByName([Clone]) [0 seconds]
[19-09-11 10:11:57:378 CEST] Sheet.getRange([1, 1, 75000, 2]) [0 seconds]
[19-09-11 10:11:57:378 CEST] Range.copyTo([Range, {contentsOnly=true}]) [0 seconds]
[19-09-11 10:11:57:379 CEST] Spreadsheet.getSheetByName([Clone]) [0 seconds]
[19-09-11 10:11:57:379 CEST] Sheet.getRange([1, 1, 75000, 2]) [0 seconds]
[19-09-11 10:12:04:314 CEST] Range.getValues() [6.934 seconds]
[19-09-11 10:12:04:445 CEST] Sheet.getRange([1, 1, 75000, 2]) [0 seconds]
[19-09-11 10:12:16:389 CEST] Range.setValues() [11.798 seconds]
[19-09-11 10:12:16:390 CEST] Spreadsheet.getSheetByName([Clone]) [0 seconds]
[19-09-11 10:12:16:950 CEST] Spreadsheet.deleteSheet([Sheet]) [0.56 seconds]
[19-09-11 10:12:18:021 CEST] Execution succeeded [26.613 seconds total runtime]
We hope that this new procedure is useful for you, but do not hesitate to ask for more help if you need it.
UPDATE II: copyTo
To improve the previous code, I implemented the method copyTo
due to its higher performance. This new code will start duplicating the range and deleting the formulas in the same spreadsheet (as the first update does). It will later copy the data from that clone sheet to the destination spreadsheet. Finally, it will copy the values from the imported clone sheet to the main sheet. I added this last functionality because I assume that you will want to move the data around in the destination spreadsheet.
function copySheet3() {
var ss1 = SpreadsheetApp.openById('{ORIGINAL SPREADSHEET}');
var ss2 = SpreadsheetApp.openById('{COPY SPREADSHEET}');
ss1.insertSheet('Clone');
var cloneSheet = ss1.getSheetByName('Clone');
var dataRange = ss1.getSheetByName('Sheet1').getRange(1, 1, 75000, 2);
var destinationRange = ss1.getSheetByName('Clone').getRange(1, 1, 75000, 2);
dataRange.copyTo(destinationRange, {
contentsOnly: "true"
});
cloneSheet.copyTo(ss2);
ss1.deleteSheet(cloneSheet);
var cloneSheet = ss2.getSheetByName(
'Copy of Clone'); // Clone on the destination
var tempRange = cloneSheet.getRange(1, 1, 75000, 2);
var finalRange = ss2.getSheetByName('Sheet1').getRange(1, 1, 75000, 2);
tempRange.copyTo(finalRange, {
contentsOnly: "true"
});
ss2.deleteSheet(cloneSheet);
}
As we all can see on the execution transcript, this approach minimizes the elapsed time even more than previous options:
[19-09-16 10:24:04:036 CEST] Starting execution
[19-09-16 10:24:04:113 CEST] SpreadsheetApp.openById([{ORIGINAL SPREADSHEET}]) [0.071 seconds]
[19-09-16 10:24:04:444 CEST] SpreadsheetApp.openById([{COPY SPREADSHEET}]) [0.33 seconds]
[19-09-16 10:24:06:150 CEST] Spreadsheet.insertSheet([Clone]) [1.705 seconds]
[19-09-16 10:24:06:525 CEST] Spreadsheet.getSheetByName([Clone]) [0.374 seconds]
[19-09-16 10:24:06:525 CEST] Spreadsheet.getSheetByName([Sheet1]) [0 seconds]
[19-09-16 10:24:06:526 CEST] Sheet.getRange([1, 1, 75000, 2]) [0 seconds]
[19-09-16 10:24:06:527 CEST] Spreadsheet.getSheetByName([Clone]) [0 seconds]
[19-09-16 10:24:06:528 CEST] Sheet.getRange([1, 1, 75000, 2]) [0 seconds]
[19-09-16 10:24:06:528 CEST] Range.copyTo([Range, {contentsOnly=true}]) [0 seconds]
[19-09-16 10:24:12:755 CEST] Sheet.copyTo([Spreadsheet]) [6.226 seconds]
[19-09-16 10:24:13:271 CEST] Spreadsheet.deleteSheet([Sheet]) [0.515 seconds]
[19-09-16 10:24:13:272 CEST] Spreadsheet.getSheetByName([Copy of Clone]) [0 seconds]
[19-09-16 10:24:13:273 CEST] Sheet.getRange([1, 1, 75000, 2]) [0 seconds]
[19-09-16 10:24:13:273 CEST] Spreadsheet.getSheetByName([Sheet1]) [0 seconds]
[19-09-16 10:24:13:274 CEST] Sheet.getRange([1, 1, 75000, 2]) [0 seconds]
[19-09-16 10:24:13:275 CEST] Range.copyTo([Range, {contentsOnly=true}]) [0 seconds]
[19-09-16 10:24:15:594 CEST] Spreadsheet.deleteSheet([Sheet]) [2.319 seconds]
[19-09-16 10:24:15:767 CEST] Execution succeeded [11.553 seconds total runtime]
Please, let all of us know if this new code sample helped on your project. Feel free to add more ideas or to share the execution transcript for more development.
回答2:
Update II works really much faster, I've had though several problems with the steps:
ss1.insertSheet('Clone');
and
cloneSheet.copyTo(ss2);
Adjusting the script, so the "Clone" sheet in the original Spreadsheet is not deleted lter and hence this has not to be created any more at the beginning, the script is running smooth (execution under 4 seconds).
function copySheet5() {
var ss1 = SpreadsheetApp.openById([{ORIGINAL SPREADSHEET}]);
var ss2 = SpreadsheetApp.openById([{COPY SPREADSHEET}]);
var cloneSheet = ss1.getSheetByName('Clone');
var dataRange = ss1.getSheetByName('Sheet1').getRange(4, 1, 481, 52);
var destinationRange = ss1.getSheetByName('Clone').getRange(1, 1, 481, 52);
dataRange.copyTo(destinationRange, {
contentsOnly: "true"
});
cloneSheet.copyTo(ss2);
var cloneSheet = ss2.getSheetByName(
'Copy of Clone'); // Clone on the destination
var tempRange = cloneSheet.getRange(1, 1, 481, 52);
var finalRange = ss2.getSheetByName('Sheet1').getRange(520, 2, 481, 52);
tempRange.copyTo(finalRange, {
contentsOnly: "true"
});
ss2.deleteSheet(cloneSheet);
}
Thanks a lot for all the help!
来源:https://stackoverflow.com/questions/57831414/getdata-script-time-out-only-when-time-triggered