I've loaded a 66 MB csv file to Fusion Tables. It's about 475k rows long and 12 columns wide.
I'm using Google Apps Script and trying to query the data within there.
One of the columns is the name of the person who that data belongs to, for instance, Joe.
If I want to pull all of Joe's data out so I can display it to him in a nice format, I'm using this query:
var tableId = my_table_id;
var sql1 = "SELECT * FROM " + tableId + " WHERE 'User' = 'Joe'";
var result = FusionTables.Query.sql(sql1,{hdrs : false});
The issue is that Joe has about 52k lines of data. I want to return it so I can load it to a datable and the user can sort through it and view all of the data. I get one of two errors:
If I run the query as above I get:
Response Code: 413. Message: response too large.
If I just try to select it all (SELECT * FROM tableId), I get:
Response size is larger than 10 MB. Please use media download
For media download, I've tried specifying alt : 'media' in the parameters, but I don't think that works within Google Apps script (I can't find documentation on it anywhere).
I have also tried looping through the queries, so select * limit 0,1000, then select * limit 1001,2000, ect. However, fusion tables SQL doesn't seem to support that either.
At this point, I may just leave the CSV in my drive, parse it in on the fly, but that's my last resort. Any advice would be appreciated!
So I think I figured this out. I'm sure it's not the most elegant solution, but here goes:
I run a quick query to check the count() for Joe to see how many records there are and only run loops if needed. I set the max to 40,000 records:
var total_rows_query = "SELECT COUNT() FROM " + tableId + " WHERE 'User' = " + username;
var total_rows = FusionTables.Query.sql(total_rows_query,{hdrs : false}).rows[0][0];
If the total rows are greater than I want, I use the OFFSET and LIMIT parameters to structure the queries:
max_rows = 40000;
if(total_rows > max_rows){
var counter = 0;
//adding in a zero to the ranges since the last query will be the offset of 0, meaning all of them
var ranges = [0]
while(counter + chunk_size < total_rows){
counter = counter + chunk_size;
ranges.push(counter)
}
ranges.push(total_rows)
//Now ranges is an array with zero at the beginning, and counting up by the chunk size I want, ending with the total_rows for the user as the last oen
//This is the array that will be output after concating
var output = []
//looping through the array, setting the offset to the first item, and the limit to the next item minus the first
for(i=0;i<ranges.length-1;i++){
var offset = ranges[i]
var limit = ranges[i+1] - offset
var query = "SELECT * FROM " + tableId + " WHERE 'User' = '" + username + "' OFFSET " + offset + " LIMIT " + limit;
output = output.concat(FusionTables.Query.sql(query,{hdrs : false}).rows)
}
}else{
//if the count is less or equal to the chunk size, just run the one query
var query = "SELECT * FROM " + tableId + " WHERE 'User' = " + username;
var output = FusionTables.Query.sql(query,{hdrs : false}).rows
}
The last thing to note is that if the username is two words, for instance 'John Smith', you may need to add in quotes around your username, so instead of
var total_rows_query = "SELECT COUNT() FROM " + tableId + " WHERE 'User' = " + username;
It would be:
var total_rows_query = "SELECT COUNT() FROM " + tableId + " WHERE 'User' = '" + username + "'";
I spend the last two days trying to figure this out, so I hope it helps someone out there!
Related
Ok so I've been pretty much learning on my own and I ran into a problem that I cant seem to find a solution for.
The main goal is I have a google sheet with a start and end date that the user can change, the script pulls those dates and uses them in a MySQL query to pull the data between that date range.
Example: start date = 10/1/2021, end date = 10/22/21
Query: "select * from Table, where table.date >= start date AND table.dat <= end date"
See my code example below:
======================================================
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheetByName('DR_Campaign_Report');
var getStartDate = sheet.getRange(1,2).getValue();
var startDate = Utilities.formatDate(getStartDate,"GTM","MM/dd/yyyy");
var getEndDate = sheet.getRange(1,5).getValue();
var endDate = Utilities.formatDate(getEndDate,"GTM","MM/dd/yyyy");
var conn = Jdbc.getConnection(url, username, password);
var stmt = conn.createStatement();
var results = stmt.executeQuery
( 'SELECT m.Campaign as "Campaign",\n' +
'count(m.Campaign) as "Leads",\n' +
'count(m.Duplicate) as "Dups",\n' +
'count(m.Campaign) - count(m.Duplicate) as "Valid Leads",\n' +
'count(m.AppSet) as "Appts",\n' +
'SUM(IF(m.ZepID != "",1,0)) as "Transferred Appts"\n' +
'FROM intakeMani m\n' +
'WHERE date(m.IncomingDate) >= date('startDate') and date(m.IncomingDate) <= date('endDate')\n' +
'OR date(m.AppSet) >= date('startDate') and date(m.AppSet) <= date('endDate')\n' +
'GROUP BY m.Campaign with rollup'
);
The Error is here in the WHERE clause when its attempting to pull the google script variables startDate and endDate.
'WHERE date(m.IncomingDate) >= date('startDate') and date(m.IncomingDate) <= date('endDate')\n' +
'OR date(m.AppSet) >= date('startDate') and date(m.AppSet) <= date('endDate')\n' +
I attempted the double "'startDate'" and is still errors. see attached pic.
I fixed it, I had double " in the var getStartDate/GetEndDate fields and need to add " to the query "'+startdate+'"
Thank you for your help.
I'm trying to combine date and time into one element in google app script array. This is for converting data from google sheet into google calendar.
I've 4 elements in my array; title, date, start time, end time. Each of them were retrieved by .getValues from google sheet.
title1 | Aug 08,2019 | 7:30 | 8:25
title2 | Aug 10,2019 | 8:30 | 9:25
I want to grab date and time from google sheet then createEvent in calendarApp.
//so with .getValues() in cArr variable from the table above I tried this code:
for (var i = 0; i <= cArr.length; i++){
CalendarApp.getCalendarById("myCalendarID").createEvent(cArr[i][0],cArr[i][2],cArr[i][2]);
};
The script were successfully run without error. But the event didn't appear in my calendar. I assume the events ever create in 1899 since it didn't specified the date in element [2] and [3].
Through some research, my best guess is to modify the array elements to be in 'MMM dd/yyyy, HH:mm' for both element [1] and [3]. But I just can't find a solution to do it. In the end, I want the result array like
[
["title1","Aug 08/2019, 7:30","Aug 08/2019, 8:25"],
["title2","Aug 10/2019, 8:30","Aug 10/2019, 9:25"]
]
Before I use this new array in .createEvent.
You can use the getDisplayValues() function [1] to obtain the string value of the cell, from this get the date info and create a Date object with that. Here is the code for that:
var cArr = sheet.getRange(13, 3, 2, 4).getDisplayValues();
for (var i = 0; i < cArr.length; i++){
var month = cArr[i][1].substring(0, 3);
var day = cArr[i][1].substring(4, 6);
var year = cArr[i][1].substring(7);
var startMinutes = cArr[i][2].substr(-2);
var startHours = cArr[i][2].substring(0, 2);
var endMinutes = cArr[i][3].substr(-2);
var endHours = cArr[i][3].substring(0, 2);
var startDate = new Date(month + " " + day + ", " + year + " " + startHours + ":" + startMinutes + ":00");
var endDate = new Date(month + " " + day + ", " + year + " " + endHours + ":" + endMinutes + ":00");
CalendarApp.getCalendarById("[mail]").createEvent(cArr[i][0], startDate, endDate);
};
[1] https://developers.google.com/apps-script/reference/spreadsheet/range#getdisplayvalues
Thank you so much for suggestions.
I found the work around solution for my problem. I will share it here for references.
The data retrieved from googlesheet were converted in to date object so the idea is to create a new string containing date, month, year, time(hour and minute) using concatenation then apply the string on new Date() function.
var day = Utilities.formatDate(cArr[1],"GMT+07:00","MMM dd");
var year = Utilities.formatDate(cArr[1],"GMT+07:00","yyyy");
var ST = Utilities.formatDate(cArr[2],"GMT+07:08","HH:mm");
var ET = Utilities.formatDate(cArr[3],"GMT+07:08","HH:mm");
//then I concatenate them together
var StartTime = new Date(day + " " + ST + " " + year);
var EndTime = new Date(day + " " + ET + " " + year);
I did this under .map() function on cArr for a better operation time then using a for loop to create event in CalendarApp.getCalendarById("myCalendarID").createEvent().
PS. I don't know why the time zone has to be GMT+7:08 but this is from my trials and errors to get this time-shift to work best for my project. Also I tried to make it correct to the second digit, but the object turned into 'Jan 1 1899 8:00:00' when I tried with GMT+7:07:48.
Our client's online shop uses Opencart, for marketing purposes they wanted to sync Opencart orders automatically with Google Sheets. I modified a script in google sheets to extract customer orders from the past year (based on https://www.transpacific-software.com/blog/opencart-to-google-docs-pull-and-sync-data-auto-through-scripting and https://gist.github.com/pradeepbheron/e6129c9fd9bc74e814d0)
The SQL query is:
SELECT order_id, firstname AS first_name, lastname AS last_name,
email, date_added AS order_date, CONCAT("£", FORMAT(total,2))
order_value, payment_address_1 AS billing_address_1,
payment_address_2 AS billing_address_2, payment_city AS
billing_city, payment_postcode AS billing_postcode,
payment_country AS billing_country, items_purchased FROM
(
SELECT order_id, firstname, lastname, email, date_added,
total, payment_address_1, payment_address_2, payment_city,
payment_postcode, payment_country
FROM ocbw_order
GROUP BY order_id
) AS A
JOIN (
SELECT order_id AS product_order_id, GROUP_CONCAT(name
SEPARATOR ", ") AS items_purchased
FROM ocbw_order_product
GROUP BY product_order_id
) AS B
ON A.order_id=B.product_order_id
WHERE date_added >= DATE_SUB(NOW(),INTERVAL 1 YEAR)
AND firstname != ''
It runs fine in phpMyAdmin but Google Script Editor generates an "Exceeded maximum execution time" error.
It looks like there are 7357 rows (exported from myPhpAdmin). Is there a better way to write the query? Also I am trying to rename the column headers but can only two works, i.e:
GROUP_CONCAT(name SEPARATOR ", ") AS items_purchased
and
CONCAT("£", FORMAT(total,2)) order_value
Any thoughts
QUICK UPDATE: Fri Nov 9 11:54:03 2018
1) As request by #ThisGuyHasTwoThumbs, here is a screenshot of the explain table result
2) I looked into the Best Practices doc mentioned by #bcperth. I tried to rewrite the google sheets script but ran into issues.
Here is the amended script.
function p1MySQLFetchData() {
// Change it as per your database credentials
var conn =
Jdbc.getConnection('jdbc:mysql://[dbHostIp]:3306/[dbName]',
'[dbUsername]', '[dbPassword]');
var stmt = conn.createStatement();
var start = new Date(); // Get script starting time
//change table name as per your database structure
var rs = stmt.executeQuery('[sqlQuery]');
// It sets the limit of the
// maximum nuber of rows in a ResultSet object
// Returns the currently active spreadsheet
var doc = SpreadsheetApp.getActiveSpreadsheet();
var cell = doc.getRange('a1');
var row = 0;
// Mysql table column name count.
var getCount = rs.getMetaData().getColumnCount();
// Create array to hold mysql data
var tempArray = [];
// get row and column count for later
var colCount = getCount;
// ATTEMPT TO GET ROW COUNT 1
//rs.last();
//var rowCount = rs.getRow();
//rs.beforeFirst(); // resets rs cursor
//Logger.log(RowCount);
// DOESN'T WORK! result => ReferenceError: "RowCount" is not
// defined. (line 28, file "Code")
// ATTEMPT TO GET ROW COUNT 2
//var rsCount = stmt.executeQuery('SELECT COUNT(*) AS rowcount FROM
//[sqlQuery]);
// It sets the limit of the maximum number of rows in a ResultSet
// object
//rsCount.next();
//var rowCount = rsCount.getString("rowcount");
//Logger.log(rowCount);
// DOESN'T WORK! result => 0
// Build TempArray using MySQL data
for (var i = 0; i < getCount; i++){
tempArray[0][i] = rs.getMetaData().getColumnName(i+1);
// DOESNT WORK => ERROR
// TypeError: Cannot set property "0.0" of undefined to "order_id".
// (line 39, file "Code")
//Logger.log(rs.getMetaData().getColumnName(i+1));
}
var row = 1;
while (rs.next()) {
for (var col = 0; col < rs.getMetaData().getColumnCount();
col++) {
tempArray[row][col] = rs.getString(col + 1);
//Logger.log(rs.getString(col + 1));
}
row++;
}
// BELOW DOESNT AS I CANT GET A ROW COUNT (SEE ABOVE)
// Fill Spreadsheet from tempArray
//for (var row = 0; row < rowCount; row++) {
//for (var col = 0; col < colCount; col++) {
//cell.offset(row, col).setValue(tempArray[row][col + 1]);
//}
// }
rs.close();
stmt.close();
conn.close();
var end = new Date(); // Get script ending time
Logger.log('Time elapsed: ' + (end.getTime() - start.getTime()));
// To generate script log. To view log click on View -> Logs.
}
But as you can see from the comments, I get loads of errors. Am not sure what to do next.
UPDATE Fri Nov 30 15:26:14 2018
In answer to #Tedinoz comment below.
PhpMyAdmin generates 6862 results and the query took 13.3074 seconds.
When I ran the script in Googles Script Editor, it took around 2 minutes 30 to complete and only pulls 6348 records (92.5%). The records stop after 3rd October 2018.
Regarding your suggestions:
1) I tried running a modified script, setting the INTERVAL to:
1 MONTH => I get no results in Google (it should be 529)
2 MONTH => I get 14 results (should be 1029)
3 MONTH => I get 299 results (should be 1669).
They all took about 4-7 second in myPhpAdmin vs 5 - 20 seconds for Google Script Editor
2) Do you mean exporting a csv from phpMyAdmin and importing to Google Sheets? Well I did that and it works fine.
Another thing I have noticed is that the order_id's in Google Sheets don't match that from phpMyAdmin. Weird.
Select records based on the criteria shown
Is there any way i can select records from MySQL based on this criteria apart using the many if else statements.
Actually what i have in mind is below
if CurrentLevel.SelectedItem <> Nothing AND Programme.SelectedItem = Nothing AND Gender.SelectedItem = Nothing Then
myconnection.Open()
Dim SelCmd as SqlCommand = New SqlCommand("Select * From StudentsList Where CurrentLevel = '"& CurrentLevel.SelectedItem &"'",myconnection)
And I'll have to do it for all the possible outcomes.
Which makes the code very lengthy and tiresome to write.
Is there a shorter way of performing this search because I'll perform another search with almost 16 criteria.
The question is not entirely clear to me. You won't need a new if statement for all the criteria. You can achieve this by modifying the SQL query itself. One of the main purpose of SQL is to get data that match certain criteria.
SELECT * FROM StudentsList WHERE `CurrentLevel` = "level" AND `gender` = "male" AND `programme` = "something"
The above SQL query should give you a basic idea. It will select the rows which have CurrentLevel as level, gender as male and programme as something only, the rest will be ignored.
EDIT:
I don't know VB. Here is a quick, dirty example in C# which will help you understand the basic logic behind this.
string sqlQuery = "SELECT * FROM StudentsList ";
if(currentLevelDropDown.SelectedItem.Text != "")
{
sqlQuery + "WHERE CurrentLevel = " + currentLevelDropDown.SelectedItem.Text;
}
if(ProgrammeDropDown.SelectedItem.Text != "")
{
sqlQuery + " AND WHERE programme = " + ProgrammeDropDown.SelectedItem.Text;
}
//Final Query becomes: SELECT * FROM StudentsList WHERE CurrentLevel = userSelectedOption AND WHERE programme = userSelectedProgrammeOption
//Finally execute the sqlQuery
There are 30 tables(categories) all with the same structure storing news items with a siteID field to filter on a particular client.
The client select which tables(categories) they show by setting the field visible(tinyint) field to 1 or 0.
I have the following test MYSQL which works okay. I am using Applicationcraft.com so the syntax is different than standard MYSQL but you can see the query.
function _getAllData(cObj,p){
var result = [];
console.log('started');
selectObj=cObj.select().from('schoolNews').order('newsIDDESC').where('siteID=?',p.siteID);
result[0] = cObj.exec(selectObj);
selectObj=cObj.select().from('schoolDocs').order('newsIDASC').where('siteID=?',p.siteID);
result[1] = cObj.exec(selectObj);
return result;
}
So I have an array with the results of each table in result[0] & result[1].
So I created the following to :
function _getAllData(cObj,p){
var result = [];
console.log('started');
selectObj=cObj.select().from('schoolNews').order('newsIDDESC').where('siteID=?',p.siteID).where('visible=?',1);
result[0] = cObj.exec(selectObj);
selectObj=cObj.select().from('schoolDocs').order('newsIDASC').where('siteID=?',p.siteID).where('visible=?',1);
result[1] = cObj.exec(selectObj);
selectObj=Obj.select().from('schoolNews_copy').order('newsIDDESC').where('siteID=?',p.siteID).where('visible=?',1);
result[2] = cObj.exec(selectObj);
selectObj=cObj.select().from('schoolNews_copy').order('newsIDDESC').where('siteID=?',p.siteID).where('visible=?',1);
result[3] = cObj.exec(selectObj);
selectObj=cObj.select().from('schoolNews_copy').order('newsIDDESC').where('siteID=?',p.siteID;
result[4] = cObj.exec(selectObj).where('visible=?', 1);
upto result[30].
I have populated schoolNews_copy with 1000 records and run the query from my app.
I am getting a timed out error.
Is this because.
query the same table causes the problem.
This is the wrong approach all together.
If not what is the best approach.
Is there a way to query every table in a single statement and populate the results into an array named results.
So the result I need is an example array :
result[0] has data visible set to 1
result[1] has data visible set to 1
result[2] has data visible set to 0
I have now restructured the table as you said. And using joins can get all the info I need in one query.
SELECT * FROM categories INNER JOIN allNews on allNews.catID = categories.catID WHERE categories.visible = 1 AND categories.siteID = '+p.siteID;
MrWarby.