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:

  • to demonstrate how to extract a column of data, or an entire table from an sqlite database

  • how to then upload that data to a google sheet

  • how to download data from a google sheet and prepare it for SQLite

  • how to insert your data as a column or table to an SQLite database

  • finally, and slightly unrelated to AI2, a demo on how to work with relational data on Google Sheets

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 helper extension, ListFixer, 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

  • On google drive, create a new google sheet in your preferred location

  • We are going to create a web app project bound to the spreadsheet

  • On the spreadsheet menu bar, click Extensions, then Apps Script

  • A new project should open, give it a name.

  • The script used is very similar to one I used here but I have added a few things:

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);
}

  • Copy this, and replace all the existing code with it.

  • Publish as a web app, executing as your google account and accessible by "Anyone" (new editor) / "Anyone, even anonymous"(legacy editor)

  • Back to the spreadsheet, add a new sheet, and name it Column. Add another sheet and name it DataTable.

  • We now have everything we need, time to test the upload of data from AI2 to the spreadsheet.

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....

  • Add this to the web app (after the last curly brace):

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);
}

  • Now republish the web app to a new version.

  • We can now use the web component "Get" to return the data

  • Now then, the column data is a list of lists, we do not want this for putting the data back as VALUES in a column in a table in SQLite, so we have to remove all the square brackets, and add back a square bracket at the beginning and end

  • For the Table Data, we need to do something similar, but this time replacing all the square brackets with rounded brackets, and removing the outer brackets completely

  • Data is now all prepared for inserting to a table in an sqlite database

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.

  • We need to first have a database loaded and OPEN

  • Next we create a new column:

ALTER TABLE data ADD COLUMN sheetData TEXT

  • (read here for more about ALTER TABLE - there are some limitations)

  • The table called data in peopleCodes.db should now have a new column called sheetData

  • We can now insert (update) our prepared data:

    • The column data is fetched from the google sheet

    • the square brackets are removed

    • the string is converted to a list

    • Iterate over each value of the list, "UPDATING" the column sheetData, where the id matches the item index in the list

    • display the updated table

Insert a new Table and add the data from google sheet

  • Make sure we have the database OPEN

  • Create a new table called sheetData, with columns for id,full_name,code

  • For this example, we won't set a primary key, given the id numbers, although sorted, are not consecutive. Neither do we set any constraints - e.g. NOT NULL

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]]

  • Now we can call down the data from the google sheet and apply it to the new table in one hit:

    • Fetch data from google sheet

    • Replace the square brackets with rounded brackets (note: we do not have to make an AI2 list)

    • Insert all the data to the table

    • Display the table data

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.