Integrating SQLite with Google Sheets

INTRO

Having just done a load of work on SQLite, you won't be surprised to find this guide, about how you can integrate SQLite with Google Sheets from AI2!  You will be happy that you are able to work with a local database on your device, but you may wish to extract and/or back up the data to an online resource, or import a list or table to your local database, or even use google sheets functions to work with a relational database, on google sheets. 

If you have arrived at this page before doing anything with SQLite on AI2, then you should first visit: Working with SQLite  in order to get up to speed. This guide will assume you have already done this.

The aims of this guide are:

Throughout, I will work with a simple database that contains a 3 column table, called peopleCodes.db

Extracting Data from SQLite

We already have the tools to do this, we use a SELECT SQL command to extract some or all the data we need:

To extract a column of data

SELECT full_name FROM data WHERE full_name LIKE 'B%' LIMIT 30

This will return 30 names that start with B as a flat AI2 list:

In order to return all the data in the column just use:

SELECT full_name FROM data

Now, we have a problem here. If we send this up to  our google apps script using postText, it will create one row, with many columns. We want a column, so we will have to convert this flat list into a list of lists before sending to google. This is simply done:

To extract an entire table of data

SELECT * FROM data WHERE full_name LIKE 'D%' LIMIT 10

This will return the id, the full_name and the code columns where the full_name starts with D as an AI2 list of lists.

This list of lists is all ready to be sent to google.

In order to return the entire table, just use:

SELECT * FROM data

If you have many tables in your database, you will need to extract each one individually.

You will notice the use of the ListFixer block (now incorporated in the SimpleSQLite extension [V3]), because we are getting back a list of lists from the query, and it needs conversion to an AI2 list. ( I could have used it for the first query as well, but this was not needed as the query was returning a flat list).

You might also want to get the column names, and add these to the top of your list:

SELECT name FROM PRAGMA_TABLE_INFO('data');

which returns:

["id","full_name","code"]


Send column or table to google sheets from AI2

We use the web component with the url set to a google apps script web app (I'll get to this in a minute...), and the list data set to PostText. Notice that we set the name of the sheet in the url section.This ensures the data gets sent to the correct sheet in the spreadsheet

Setup a spreadsheet and web app

function doPost(e) {
  var msg = "Data Upload Failed";
  var ss = SpreadsheetApp.getActive();
  var sh = ss.getSheetByName(e.parameter.sheet);
  var data = JSON.parse(e.postData.contents);
  for (var i=0;i<data.length;i++) {
  sh.appendRow(data[i]);
  }
  var lr = sh.getRange(sh.getLastRow(), 1).getValue();
  var le = data[(data.length - 1)][0];
  if (lr == le) {
  msg = "Data Upload Completed";
  }
  return ContentService.createTextOutput(msg); 
}

It works..... :)

Download data from google sheet to your App

We need to add another section to the web app for this, we could use a gviz query or export?format=csv, but it is better to return the data as a stringified JSON, which will make it easier to carry out the bracket substitution later....

function doGet(e) {
  var ss = SpreadsheetApp.getActive();
  var sh = ss.getSheetByName(e.parameter.sheet);
  var data = JSON.stringify(sh.getDataRange().getDisplayValues());
  return ContentService.createTextOutput(data);
}

Inserting a Column to an existing table

There does not appear to be a method to "insert" a list of column values. For this example I will use UPDATE and the list index number to set the id. This will place the new values from the top of the table. There also does not appear to be a method to UPDATE with multiple values, so we will have to iterate singly.

ALTER TABLE data ADD COLUMN sheetData TEXT

Insert a new Table and add the data from google sheet

CREATE TABLE IF NOT EXISTS sheetData(id INTEGER,full_name TEXT,code TEXT)

Check the table is there with the following two commands:

SELECT name FROM sqlite_master WHERE type='table'

result: ["data", "android_metadata", "sheetData"]

and

SELECT * FROM pragma_table_info('sheetData')

result: [[0, id, INTEGER, 0, null, 0], [1, full_name, TEXT, 0, null, 0], [2, code, TEXT, 0, null, 0]]

SUMMARY

That more or less concludes the main part of this guide, which hopefully demonstrates how to exchange SQLite data with a google sheet and vice-versa. There are probably many other methods that can be used to achieve the same thing, and I have only really scratched the surface of what may be possible. As I indicated at the top, this is all about backup, modification or maintaining an online reflection of your on-device resource. If your SQLite database is static, or you prefer to use a tool like DB Browser for SQLite then you may not need to use the above. I do hope this has been useful and not too long or confusing ;)

BONUS SECTION

Using a Google Sheet with Relational Data


As promised, i intend to demonstrate how you can use google sheets to carry out queries on relational data. Why? Well, one of the main purposes of a database is to work with many tables and to relate one table's data to another, and to then bring the data together with queries. Google Sheets tends to operate with flat files, because it is not as easy to set up the relationships between tables. However, if you are going to consider using google sheets as a location to store your sqlite data, you may as well be able to query it, right ?

To demonstrate, I have created two sheets one called Bands, which holds a list of rock bands from the '70's, the other called Albums, which holds a list of albums by those bands, but indicates which Band created the album by way of an ID.  Here are the two tables:

Bands

Albums

This is how you might set things up in an SQLite database. In SQLite, you might then generate a query on these two tables using a (LEFT)JOIN to return a listing of bands and their albums. The basic query would look something like this:

SELECT Band FROM Bands LEFT JOIN Albums ON (Bands.id = Albums.BandId) ORDER BY Bands.Band 

and this would return an output like this:

AC/DC Let There be Rock
Deep Purple Machine Head
Deep Purple Fireball
Fleetwood Mac Rumours
Fleetwood Mac Tusk
Led Zeppelin IV
Wishbone Ash Argus
Wishbone Ash Pilgrimage


To do the same thing in google sheets requires some out of the box thinking, and in fact the main grunt work to return the data does not use the google query language at all! We start off by using a VLOOKUP function in an ARRAYFORMULA - {....} - to return all the Bands related to the Albums. This is then passed through a FILTER function to create a listing of Bands and Albums. Finally use the QUERY function to display the Band first then the Album for each record, and to order them alphabetically. The final function looks like this:

=query(FILTER({Albums!B$2:C,VLOOKUP(Albums!C$2:C,{Bands!A$2:A,Bands!B$2:B},{2},false)},Albums!B$2:B<>""),"SELECT Col3,Col1 ORDER BY Col3")

Once this is done, it is then possible to set up a drop down selector (containing all the band names), and to use the query to return just the albums by a particular band:

=if(K24="","",query(FILTER({Albums!B$2:C,VLOOKUP(Albums!C$2:C,{Bands!A$2:A,Bands!B$2:B},{2},false)},Albums!B$2:B<>""),"SELECT Col1 WHERE Col3 = '"&K24&"' ORDER BY Col1"))

Credits go to the resources found here and here.