0

I am trying to use Script editor(Apps script) to stream BigQuery results into google sheets. I'm following these [document][1] .

The issue I have, the data doesn't load fully, it hangs. I have a large number of rows(more than 12 rows). I think I need to optimise the "Append the results" section with some sort of batch update. Now it's a loop which i'm guessing is not very efficient. I cannot figure it out. I tried to use ".next()" but get an error, function didn't exist. I use these [document][2].

How can I optimise the append results section? Heres the whole code(later i've included just the part im looking to modify):

  
  var ui = SpreadsheetApp.getUi();
  ui.createMenu("Update")
      .addItem('Update','update')
      .addToUi();
}


function update() {
  
  run1("\"Filter1\"","\"FilterA\"","Sheet1);
  run1("\"Filter2\"","\"FilterB\"","Sheet2");

  
};


function run1(filter1,filter2,output) {
  
  var projectId = 'xxx';
  var request = {
    useLegacySql: false,
    useQueryCache: false,
    query: 'select * from table ' +
           'where a1.col1 = ' + filter1 + ' and a1.col2  in ( ' + filter2 + ' ); ' 
     
  };
  
  var queryResults = BigQuery.Jobs.query(request, projectId);
  var jobId = queryResults.jobReference.jobId;
  


  // Check on status of the Query Job.
  var sleepTimeMs = 500;
  while (!queryResults.jobComplete) {
    Utilities.sleep(sleepTimeMs);
    sleepTimeMs *= 2;
    queryResults = BigQuery.Jobs.getQueryResults(projectId,jobId);
  }

  // Get all the rows of results.
  var rows = queryResults.rows;
  while (queryResults.pageToken) {
    queryResults = BigQuery.Jobs.getQueryResults(projectId,jobId, {
      pageToken: queryResults.pageToken
    });
    rows = rows.concat(queryResults.rows);
  }

  if (rows) {
    var spreadsheet = SpreadsheetApp.getActive();
    var sheet = spreadsheet.getSheetByName(output);
    sheet.clearContents();
    
    // Append the headers.
    var headers = queryResults.schema.fields.map(function(field) {
      return field.name;
    });
    sheet.appendRow(headers);

    spreadsheet.getSheetByName(output).getRange("C1").setValue("Modified_col_name");

    // Append the results.
    var data = new Array(rows.length);
    for (var i = 0; i < rows.length; i++) {
      var cols = rows[i].f;
      data[i] = new Array(cols.length);
      for (var j = 0; j < cols.length; j++) {
        data[i][j] = cols[j].v;
      }
    }

   


    sheet.getRange(2, 1, rows.length, headers.length).setValues(data);

    Logger.log("Results spreadsheet created: %s",
        spreadsheet.getUrl());
  } else {
    Logger.log("No rows returned.");
  }
};

Specifically this part of the code:

for (var i = 0; i < rows.length; i++) {
      var cols = rows[i].f;
      data[i] = new Array(cols.length);
      for (var j = 0; j < cols.length; j++) {
        data[i][j] = cols[j].v;
      }
    }
  • Did you verify that the "Specifically this part of the code" is the part that has an error, or takes too long? What troubleshooting data have you collected from your efforts? Can you share basic metadata about the actual data you are attempting to write to the sheet? (Number of columns & number of rows, total?) How much existing data is already in the Google Sheets file? etc. – tehhowch Oct 27 '21 at 11:43
  • The "Specifically this part of the code" works but it uploads about 2-3k rows at a time. I'm looking to have one batch upload for all the sql row results. – user17243359 Oct 27 '21 at 16:29
  • From your comment of `@Tanaike: I also have a different question that I was struggling with , no one else was able to give a solution : stackoverflow.com/questions/69711440/…`, I checked this question. Unfortunately, I cannot understand your question. I apologize for my poor English skill. So can I ask you about your question? Do you want to put the values from Spreadsheet to BigQuery? Or do you want to put the values from BigQuery to Google Spreadsheet? – Tanaike Jan 09 '22 at 00:08
  • @Tanaike I want to put the BigQuery output values into the google spreadsheet. Right now the for loop is appending rows slowly(example adding 2k rows at a time) making it very slow. I want to know how to modify the code to push all rows to the spreadsheet together as one update – user17243359 Jan 09 '22 at 00:58
  • Thank you for replying. I could understand that you wanted to put values from BigQuery to Spreadsheet. In this case, in your script, `row` of `if (rows) {` is the values from BigQuery, and you want to this value to Spreadsheet. But, the size of value `rows` is large. Is my understanding correct? If my understanding is correct, can you provide the sample values of `rows`? – Tanaike Jan 09 '22 at 03:56
  • @Tanaike Yes you are correct. Sorry I am unable to add a file to my post, I pasted a screenshot of some example rows. there are about 25 or 26 columns. Since the SQL results are too large, I created 4 separate tabs(in the sample code I only showed 2 tabs in run1-sheet 1 and sheet 2) . But each tab still has 15k-20k results. I need to rewrite the code to make it update as a whole batch instead of the "for loop" which is very slow/times out. – user17243359 Jan 09 '22 at 04:20
  • Thank you for replying. I have to apologize for my poor English skill, again. Unfortunately, I cannot understand your reply. In order to check the cost of your script, I thought that it is required to know the sample value of `rows` in your script. In this case, the sample is not required to be the same with the original data. But it is required to know the structure of the value of `rows`. I apologize for this. – Tanaike Jan 09 '22 at 04:23
  • @Tanaike How can I provide the structure to you? Stackoverflow doesnt allow me to attach the sample rows in a google sheets file – user17243359 Jan 09 '22 at 04:26
  • For example, can you provide it as the text retrieved from `console.log(JSON.stringify(rows))`? – Tanaike Jan 09 '22 at 04:28
  • @Tanaike, so sorry, I am very poor with google sheets apps script and Javascript. I have attached output of rows in the post above. please let me know if that is what you need – user17243359 Jan 09 '22 at 04:41
  • Thank you for replying. From your additional information, I proposed an answer. Could you please confirm it? If that was not useful, I apologize. – Tanaike Jan 09 '22 at 05:34

2 Answers2

1

I believe your goal is as follows.

  • You want to reduce the process cost of your script.

In this case, how about the following modification?

Modified script:

Before you use this script, please enable Sheets API at Advanced Google services.

From:

if (rows) {
  var spreadsheet = SpreadsheetApp.getActive();
  var sheet = spreadsheet.getSheetByName(output);
  sheet.clearContents();
  
  // Append the headers.
  var headers = queryResults.schema.fields.map(function(field) {
    return field.name;
  });
  sheet.appendRow(headers);

  spreadsheet.getSheetByName(output).getRange("C1").setValue("Modified_col_name");

  // Append the results.
  var data = new Array(rows.length);
  for (var i = 0; i < rows.length; i++) {
    var cols = rows[i].f;
    data[i] = new Array(cols.length);
    for (var j = 0; j < cols.length; j++) {
      data[i][j] = cols[j].v;
    }
  }

 


  sheet.getRange(2, 1, rows.length, headers.length).setValues(data);

  Logger.log("Results spreadsheet created: %s",
      spreadsheet.getUrl());
} else {
  Logger.log("No rows returned.");
}

To:

if (rows) {
  var headers = queryResults.schema.fields.map(function (field) {
    return field.name;
  });
  var data = [headers, ...rows.map(({ f }) => f.map(({ v }) => v || ""))];
  var spreadsheet = SpreadsheetApp.getActive();
  var sheet = spreadsheet.getSheetByName(output);
  sheet.clearContents();
  SpreadsheetApp.flush();
  Sheets.Spreadsheets.Values.update({ values: data }, spreadsheet.getId(), output, { valueInputOption: "USER_ENTERED" });
  // spreadsheet.getSheetByName(output).getRange("C1").setValue("Modified_col_name"); // I'm not sure about this line.
  Logger.log("Results spreadsheet created: %s", spreadsheet.getUrl());
} else {
  Logger.log("No rows returned.");
}

Reference:

Tanaike
  • 181,128
  • 11
  • 97
  • 165
0

You need to use more filters in the query or specify less number of columns. Because one of the errors you could have is that the size of the file is more than 10MB. Because the limit of rows is 50,000 rows in this case you get an error between 12,000 or 15,000 rows. You can Split the data in several google sheets.

Here you can see some solutions and you can also see more documentation in this link.

Your query results might be too large. Your query will fail if:

Pivot tables have over 50K results. To reduce your query results, you can:

  • Use filters to limit results
  • Limit the number of rows per breakout
  • Turn off “show totals” when adding rows, columns, values, and filters
  • Results’ size is more than 10MB. To reduce size, return fewer rows or columns.
Raul Saucedo
  • 1,614
  • 1
  • 4
  • 13
  • Yes I have applied filters and loaded them into different sheets(about 15k rows or so in each sheet). Is there anyway the code could be modified to get all the results to load at the same time, now it loads about 2-3k records at a time which is slowing the process down – user17243359 Oct 27 '21 at 16:34
  • @user17243359 Did you modify the code? – Raul Saucedo Oct 27 '21 at 22:11
  • Yes , I added different run functions to filter the output into different tabs – user17243359 Oct 28 '21 at 01:26
  • I see .push and .next to achieve batch upload from this [example](https://www.actiondesk.io/blog/google-sheets-script-to-automatically-retrieve-sql-data). Would you happen to know how I can modify my code to achieve the same ? I cant figure out how to get the sql result set into one variable(like results from the example in the link I just provided) – user17243359 Oct 28 '21 at 01:40