Optimizing Google Scripts calls - Timeout issues - sql-server

I have a Google Apps Script that extracts data from our SQL database to display in our Google Sheets report, but I am running into an issue where the script now has timeout issues because it takes so long to run. I know there's an inefficiency with the while loop populating the new rows and column values, but how would you optimize this to run faster? I'm sure it is because I'm making Google function calls thousands of times (our database is probably thousands of rows long).
I'm a pretty amateur programmer here, I've kind of frankensteined this from other Stack Overflow responses. Judgment is expected, but any help would be appreciated! Thank you!
function getPostedF5() {
var conn = Jdbc.getConnection('jdbc:#################;databaseName=****;user=***;password=***************');
//var conn = Jdbc.getConnection(dbUrl, user, userPwd);
var start = new Date();
var stmt = conn.createStatement();
// Read up to 25000 rows of data from the table and log them.
stmt.setMaxRows(25000);
var results = stmt.executeQuery('SELECT RTRIM(IV30400.ITEMNMBR), LEFT(IV30400.ITEMNMBR, 5), RTRIM(IV30400.DOCNUMBR), IV30400.SERLTNUM, IV30400.SERLTQTY, RTRIM(IV30400.FROMBIN), IV30200.DOCDATE as Date_Created, IV30200.DEX_ROW_TS as Date_Posted FROM IV30400 LEFT JOIN IV30200 ON IV30400.DOCNUMBR = IV30200.DOCNUMBR WHERE IV30400.DOCNUMBR LIKE \'%F%\' AND IV30400.FROMBIN LIKE \'%FOR %\' AND IV30200.DOCDATE >= \'2021-01-01 00:00:00.000\' ORDER BY IV30200.DOCDATE ASC');
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Posted F5 in GP");
sheet.getRange('B1:I25000').clear();
var cell = sheet.getRange('B1'); //Begin depositing data starting in specified cell
var numCols = results.getMetaData().getColumnCount();
var row =0;
while (results.next()) {
var rowString = '';
for (var col = 0; col < numCols; col++) {
rowString += results.getString(col + 1) + '\t';
cell.offset(row, col).setValue(results.getString(col +1 ));
}
row++
Logger.log(rowString)
}
results.close();
stmt.close();
conn.close();
var end = new Date();
Logger.log('Time elapsed: %sms', end - start);
}

Try something like this instead:
let a=[];
while (results.next()) {
let r=[];
for (var col = 0; col < numCols; col++) {
r.push(results.getString(col+1));
}
a.push(r);
}
sheet.getRange(1,1,a.length,a[0].length).setValues(a);

Related

Is there a way to highlight duplicate combinations of values in two columns, regardless of the order?

I have a UFC database and I'm looking for rematches. Therefore, I need to find the duplicate combinations of names; the combinations that appear more than once. However, since the winner of the first fight could lose the rematch, I need to find duplicates regardless of the order in which they appear.
This is how my database is structured:
database example.
Fighter 1 is the winner and fighter 2 is the loser.
Here is a link to the database (got it from kaggle): https://docs.google.com/spreadsheets/d/19ISNhYFdGzgLZz1x4h2v_Q5Pq0cofw2rkUtwk3xPXGQ/edit?usp=sharing
Feel free to play around with it.
Any ideas on how to solve this?
Here is an example of the result I'd ideally want:
image example
Just a simple highlight over the duplicates.
In order to give you more precise help, it would be helpful if you could give an example of the result you want. As has been said, if there are many duplicates and you mark each group with a different colour, it can be visually confusing.
Anyway, I have written a couple of functions with Apps Script and Spreadsheet Service that you may find useful for your project.
Code 1
With these functions, you will be able to get all fights where two fighters that you previously define have participated:
function main() {
var name1 = 'Conor McGregor'
var name2 = 'Nate Diaz'
var result = findDuplicates(name1, name2)
console.log(result)
}
function findDuplicates(name1, name2) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Ark1')
var lastRow = sheet.getLastRow()
var lastCol = sheet.getLastColumn()
var range = sheet.getRange(1, 1, lastRow, lastCol).getValues()
var result = []
for (var i = 0; i < range.length; i++) {
if (range[i].includes(name1) && range[i].includes(name2)) {
result.push(range[i])
}
}
return result
}
Code 2
With this function, you can add in a third column, the same number for each pair of fighters. This way, it is very easy to change the "add a number" to "paint the cells in a colour".
function findPairs() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Ark2')
var lastRow = sheet.getLastRow()
var lastCol = sheet.getLastColumn()
var range = sheet.getRange(1, 1, lastRow, lastCol).getValues()
var indexToSkip = []
var k = 0
for (var i = 0; i < range.length; i++) {
if (!indexToSkip.includes(i)) {
var match = range[i]
indexToSkip.push(i)
sheet.getRange(i + 1, 3).setValue(k)
for (var j = i + 1; j < range.length; j++) {
if (range[j].includes(match[0]) && range[j].includes(match[1])) {
indexToSkip.push(j)
sheet.getRange(j + 1, 3).setValue(k)
}
}
k = k+1
}
}
}
References:
Apps Script
Spreadsheet Service

Google Sheet Script Help - Store/write sheet ID, sheet name, cell location for a combined range

I have created a script to combine information from several tabs to a "master elements" tab. The script pull in rows tagged with the "MS" designation and it works great. However, I also want to pull in the Sheetname, Sheet IT, and possiby a link to the source tab for each row with "MS". That information is currently not stored in any of the source tabs. Is that possible to do in script?
function combineData() {
var destination = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Masterelements');
// TO CLEAR DESTINATION TAB BEFORE REPOPULATION
destination.getRange('A2:g1000').clearContent();
//VARIABLE TO CYCLE THROUGH SPECIFIC SHEETS
var tabs = [
'A-000',
'B-123',
];
var ss = SpreadsheetApp.getActiveSpreadsheet();
for (var s = 0; s < tabs.length; s++) {
var sheet = ss.getSheetByName(tabs[s]);
Logger.log(sheet.getLastRow());
var range = sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn());
var destination = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Masterelements');
var values = range.getValues();
//Destination parameters - equivalent to destination.getRange();
var numberOfColumns = 7;
var numberOfRows = 1;
var startColumn = 1;
var startRow = 2;
var count = 0
// IDENTIFY THE FIRST ROW TO CONSOLIDATE ITEMS
var destRow = destination.getLastRow() + 1
for (var i = 0; i < values.length; i++) {
Logger.log("i is now: " + i);
Logger.log("Current row value: " + values[i][0])
if (values[i][0] == 'MS') {
Logger.log("*** Value found in cloumn: " + values[i][0] + "**** \n Count: " + i);
count++;
var rangeToCopy = sheet.getRange((i + 1), 1, numberOfRows, numberOfColumns);
var destinationRange = destination.getRange(destRow, startColumn, numberOfRows, numberOfColumns);
destRow++;
Logger.log("Next Destination row: " + destRow);
rangeToCopy.copyTo(destinationRange, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}
}
}
}
The OP supplied working code that copies rows, from two specific sheets, with a value of "MS" in Column A. However the OP also wanted:
Sheetname
Sheet ID
possiby a link to the source tab for each row with "MS"
These are fairly easy to produce, but require too much information to explain in a comment.
I've taken the OP original code. I moved and/or edited some lines to make the code slightly more efficient. I've also added some lines of code to insert the Sheet name in Masterelements:Column H, the sheet ID in Masterelements:Column I and a link to the relevant sheet in Masterelements:Column J.
I think there is scope to make the code more efficient (specifically within the loops), but this is something that the OP can consider if the "real data" execution time is unreasonable.
function so54432164() {
// setup the spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get the spreadsheet URL - required for the hyperlink
var ssurl = ss.getUrl();
//Logger.log("DEBUG: The url for this spreadsheet is "+ssurl);//DEBUG
var destination = ss.getSheetByName('Masterelements');
// CLEAR DESTINATION TAB BEFORE REPOPULATION
// clear the entire destination sheet; this is more efficient that just clearing a nominated range
destination.clearContents();
// IDENTIFY THE first ROW TO CONSOLIDATE ITEMS
var destRow = destination.getLastRow() + 1
//VARIABLE TO CYCLE THROUGH SPECIFIC SHEETS
// note: no comma after the second tab name
var tabs = [
'A-000',
'B-123'
];
//Destination parameters
// note: brought this out of the "for" loop since only need to be declared once.
var numberOfColumns = 7;
var numberOfRows = 1;
var startColumn = 1;
var startRow = 2;
// used for destination.getRange();
// Loop through the sheets
for (var s = 0; s < tabs.length; s++) {
var sheet = ss.getSheetByName(tabs[s]);
// create variables to get the Sheet Name and the sheetID
var sheetname = sheet.getSheetName();
var sheetID = sheet.getSheetId();
//Logger.log("DEBUG: sheet: "+sheetname+", SheetID: "+sheetID);//DEBUG
// get the data: the var 'range' was never used, so we can just call the values direct to 'values'.
var values = sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn()).getValues();
//LOOP through the rows in the target sheet
for (var i = 0; i < values.length; i++) {
//Logger.log("DEBUG: row = " + i+", and Column A value: " + values[i][0]);//DEBUG
// test for "MS" in Column A
if (values[i][0] == 'MS') {
//Logger.log("DEBUG: Found MS, Count: " + i);//DEBUG
// define the range to copy
var rangeToCopy = sheet.getRange((i + 1), 1, numberOfRows, numberOfColumns);
// Logger.log("DEBUG: the rangetocopy is "+rangeToCopy.getA1Notation()); //DEBUG
// Define the destination range
var destinationRange = destination.getRange(destRow, startColumn, numberOfRows, numberOfColumns);
//Logger.log("DEBUG: the destination range "+destinationRange.getA1Notation());//DEBUG
// copy the source to the target
rangeToCopy.copyTo(destinationRange, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
// create a variable for the hyperlink formula
var formula = '=HYPERLINK("' + ssurl + '#gid=' + sheetID + '")';
// Logger.log("DEBUG: the formula = "+formula);//DEBUG
// create a variable for the extra data: Sheet name, Sheet ID, and the hyperlink to the sheet.
var extradata = [
[sheetname, sheetID, formula]
];
//define the destination range and set the values
destination.getRange(destRow, numberOfColumns + 1, numberOfRows, 3).setValues(extradata);
// increment the destination row
destRow++;
}
}
}
}

Cannot call method "push" of undefined

I'm trying to make a spreadsheet to upload products in Prestashop by .csv. Everything works like a charm, but now I need to be able to make some changes in already entered products with the same spreadsheet. The only thing I came up with is to manipulate text strings from a cell into a variable array (after that I'll find a way to go forward).
Firstly I managed to combine about ~100 cells into one cell (which I accomplished with a complicated formula) but now I need the text from this cell to be separated and temporarily stored in an array variable.
This is the formula. It's joining columns in rows with a : separator and then joins those rows with a ; separator and in the end I just needed a number:
=regexreplace(regexreplace(regexreplace(concatenate(arrayformula(if($A$13:$A$50="","",if($C$13:$C$50="","",$A$13:$A$50&":"&$C$13:$C$50&if($D$13:$D$50="","",$D$13:$D$50)&":"&(ROW($A$13:$A$50)-12)&";"))))," :",":"),": ",":"),"\+","-")
This resulted in this text:
Producător:GARMIN:1;Tip:Ceas inteligent:3;Model:Vivomove HR
Premium:4;Culoare:Gold:5;Culoare curea:Light
brown:6;Greutate:56.5g:8;Rezolutie display:64x128:9;Tip
ecran:OLED:10;GPS:Da:15;Bluetooth:Da:16;Durata in regim de asteptare
(ore):168:24;Sensori:Heart RATE, Activity Tracker, Barometric
altimeter, Accelerometer, Smart notifications, Weather, Step counter,
Move bar, Calories burned, Floors climbed, Analog
hands:26;Garanție:24luni:38;
Now I need to separate everything back as it was, but by a code in Apps Script, so that I will be able to manipulate the values separately, but they would still be in a structured form.
It should be something like this (inside a two dimensional variable):
[0][0]Producător [0][1]GARMIN
[1][0]Tip [1][1]Ceas inteligent
[2][0]Model [2][1]Vivomove HR Premium
[3][0]Culoare [3][1]Gold
[4][0]Culoare curea [4][1]Light brown
[5][0]Greutate [5][1]56.5g
[6][0]Rezolutie display [6][1]64x128
[7][0]Tip ecran [7][1]OLED
[8][0]GPS [8][1]Da
[9][0]Bluetooth [9][1]Da
[10][0]Durata in regim de asteptare (ore) [10][1]168
[11][0]Sensori [11][1]Heart RATE, Activity Tracker, Barometric altimeter, Accelerometer, Smart notifications, Weather, Step counter, Move bar, Calories burned, Floors climbed, Analog hands
[12][0]Garanție [12][1]24luni
And now the main part... The next code is breaking with the error Cannot call method "push" of undefined
//var ss = SpreadsheetApp.getActiveSpreadsheet();
//var activeSheet = ss.getActiveSheet();
//var idSpreadsheet = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1zRsGMoXJzG9oht_pr3Rr24ksPqBzTZIvNcYPUKfphNI/edit#gid=1264546658").getSheetByName("RO").getDataRange().getValues();
//var idToChange = activeSheet.getRange("A12").getValue();
var row = 0;
//var userID = Session.getActiveUser();
var bufferFeatures = [{}];
bufferFeatures = idSpreadsheet[10][29];
var bufferImages = idSpreadsheet[row][27];
//var productRows = bufferFeatures.indexOf(";",0);
var testColumn = [];
var pos = 0; //here is where we start the text string
var del = 0; //here is where we find the ";" delimiter and stop slicing text string
// THIS FOR LOOP WORKS FINE
for (pos = 0; pos < bufferFeatures.length; pos = del) {
del = bufferFeatures.indexOf(";", pos);
testColumn.push(bufferFeatures.slice(pos, del));
del++;
};
var rownr = 0; //current row number.. not really using this variable
var pos1 = 0; //here is where we start the text string
var del1 = 0; //here is where we find the ":" delimiter and stop slicing text string
var columnsAndRows = [];
columnsAndRows.push([]);
var j = 0;
//THIS FOR LOOP GIVES ME TROUBLE
for (var x = 0; x <= testColumn.length; x++) {
for (pos1 = 0; pos1 + 1 < testColumn[x].length; pos1 = pos1) {
del1 = testColumn[j].indexOf(":", del1);
var theSlice = testColumn[j].slice(pos1, del1);
var theStop = testColumn[j].length;
//for some reason, I can't get this code to "push" j=2)
Logger.log("Adding " + theSlice);
columnsAndRows[j].push(theSlice);
del1++;
pos1 = del1;
Logger.log("Added")
}
Logger.log("Next row");
del1 = 0
j++;
//rownr++;
};
You need to declare columnsAndRows[j] as a array too.
columnsAndRows[j] = [];
Using split() would be much easier:
function strToArr(string) {
if (!string) {
var string = "Producător:GARMIN:1;Tip:Ceas inteligent:3;Model:Vivomove HR Premium:4;Culoare:Gold:5;Culoare curea:Light brown:6;Greutate:56.5g:8;Rezolutie display:64x128:9;Tip ecran:OLED:10;GPS:Da:15;Bluetooth:Da:16;Durata in regim de asteptare (ore):168:24;Sensori:Heart RATE, Activity Tracker, Barometric altimeter, Accelerometer, Smart notifications, Weather, Step counter, Move bar, Calories burned, Floors climbed, Analog hands:26;Garanție:24luni:38; "
}
var arr1 = string.substr(0, string.lastIndexOf(";")).split(';'); //split by ;
var arr2 = arr1.map(function(e) { return e.split(':').slice(0,2)}); //split each element of arr1 by : and return only the first two elements
Logger.log(arr2);
return arr2;
}

for loop not iterating over last element of 2D array in google app script (GAS)

I am working on a script that, in part, takes an array of names, compares each name to column A in a sheet, and returns with a row matched value in column B. (Like the vLookup command in sheets)
The setup
var ss = SpreadsheetApp.getActiveSpreadsheet();
var clientsSheet = ss.getSheetByName("Clients");
var cRow = clientsSheet.getLastRow();
var cColumn=clientsSheet.getLastColumn();
var cData=clientsSheet.getRange(1,1,cRow,cColumn).getValues(); //create array of client data
The trouble code
//put each client on their own row and add hour
for(i=0; i < client.length; ++i){
var cl = client[i]
//iterate over array of clients (column A) and hours (Column B) to find match and log the number in column B
for(j=0;j<cData.length;++j){
if (cData[j][0]==cl){
var hour = cData[j][1];
}
}
//return the matched values
Logger.log(cl+" - "+hour);
}
The var 'client' is an array that was split from a list of names in a single cell that are separated by commas (see whole code below)
At the moment it works great except that it misses the last element in the array.
for example:
if I have a sheet with two columns and three rows like so:
A 1
B 2
C 3
I would get back
A-1
B-2
C-
It is missing that last piece on the last element - it should be
A-1
B-2
C-3
I am stumped, and I know that it must be some simple little thing.
Any help would be amazing
Thanks!
The Code:
function logClients()
/*
Take data from a google form check box submissions. Check box submissions put all checked answers into a single cell separated by a comma. The function first takes the most recently submitted row, removes unneeded spaces, and splits each element into its own part of an array.
Then, the function compares each clients name in the array to a sheet with other info, such as the default number of hours we meet. It takes the clients name, the date of submission, and the hours, and logs them on a new row in two different sheets, the Hours sheet and the Trans Log sheet.
*/
var ss = SpreadsheetApp.getActiveSpreadsheet();
var logSheet = ss.getSheetByName("Log"); //Raw data from the Google form
var hourSheet = ss.getSheetByName("Hours"); //logged data for my records, separated into individual clients
var transLog = ss.getSheetByName("Trans Log"); // logged data minus "other" catagory
var clientsSheet = ss.getSheetByName("Clients"); //sheet containing all clients names and the typical hours we meet
var lRow = logSheet.getLastRow();
var hRow = hourSheet.getLastRow();
var tRow = transLog.getLastRow();
var cRow = clientsSheet.getLastRow();
var cColumn = clientsSheet.getLastColumn();
var cData = clientsSheet.getRange(1, 1, cRow, cColumn).getValues();
//get list of clients from cell and split it into an array
var Client = logSheet.getRange(lRow, 2).getValue().replace(", ", ","); //remove all spaces after a comma
var client = Client.split(",");
//get "other" information and do the same
var Other = logSheet.getRange(lRow, 5).getValue().replace(", ", ",");
var other = Other.split(",");
//check the date and set to today if nothing else has been entered
var dcell = logSheet.getRange(lRow, 4).getValue();
var date = new Date()
if (dcell == "") {} else if (dcell == "Yesterday") {
date = new Date(date.getTime() - 1 * (24 * 3600 * 1000));
} else {
date = dcell
}
var date = Utilities.formatDate(date, "GMT-8", "MM/dd/yy"); //format date
//put each client on their own row
for (i = 0; i < client.length; ++i) {
var cl = client[i]
var hour = logSheet.getRange(lRow, 3).getValue(); //hours
if (hour == !"") {
break;
}
for (j = 0; j < cData.length; ++j) {
if (cData[j][0] == cl) {
var hour = cData[j][1];
}
}
Logger.log(date + " - " + cl + " - " + hour);
hourSheet.appendRow([date,cl, hour]);
transLog.appendRow([date, cl, hour]);
}
//put each client on their own row
for (i = 0; i < other.length; i++) {
hourSheet.appendRow([date, other[i], getHour(client[i])]);
}
} //end of function
``
This is a code that I have been working on to teach myself Java and Apps-script
Yup, simple mistake.
Not all of the spaces before each name were being removed from the array, so the if statement would skip right over them because they were not a true match

My Apps Script loop is not looking at more rows than the first

I have a general spreadsheet in which Column B has a number 1-9. I want to copy each row item to the corresponding sheet I have created, sheet 1-9. However, I set my loop to begin at row 5 and continue the length of all rows in the data range but the loop does not continue past row 5 even though more rows have a "1" in column B. Can anyone help to see why my loop is not continuing? Thanks!
function GoodFunction() {
var spreadsheet = SpreadsheetApp.getActive()
var R0sheet = spreadsheet.getSheetByName('S14Allocation')
var R1sheet = spreadsheet.getSheetByName('R1-Allocations')
var ss = spreadsheet;
var s = R0sheet
var targetSheet = R1sheet
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
var r = s.getDataRange();
var numColumns = s.getLastColumn();
var numRows=s.getLastRow();
for (var i = 5; i <= numRows.length ; i++); {
if (s.getDataRange().getCell(i,2).getValue() === 1) {
s.getRange(i,1,1,numColumns).copyTo(target);
}
}
}
numRows is already a number (from s.getLastRow();) so use i < numRows as the condition in your for loop.
As Serge said in his comment though, the code is very confusing. There's no need for that many variables, and there are more efficient ways of dealing with multiple cells at once.
https://developers.google.com/apps-script/best_practices#batchOperations

Resources