| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | --- | 
					
						
							|  |  |  | title: WebSQL and SQLite | 
					
						
							|  |  |  | pagination_prev: demos/grid | 
					
						
							|  |  |  | pagination_next: demos/worker | 
					
						
							|  |  |  | sidebar_custom_props: | 
					
						
							|  |  |  |   type: web | 
					
						
							|  |  |  |   sql: true | 
					
						
							|  |  |  | --- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | WebSQL is a popular SQL-based in-browser database available on Chrome.  In | 
					
						
							|  |  |  | practice, it is powered by SQLite, and most simple SQLite-compatible queries | 
					
						
							|  |  |  | work as-is in WebSQL. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The public demo <https://sheetjs.com/sql> generates a database from workbook. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## WebSQL Details
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Importing data from spreadsheets is straightforward using the `generate_sql` | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  | helper function from ["Generating Tables"](/docs/demos/data/sql#generating-tables). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The Web SQL Database API is callback-based.  The following snippet wraps | 
					
						
							|  |  |  | transactions in Promise objects: | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```js | 
					
						
							|  |  |  | const db = openDatabase('sheetql', '1.0', 'SheetJS WebSQL Test', 2097152); | 
					
						
							|  |  |  | const stmts = generate_sql(ws, wsname); | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | // NOTE: tx.executeSql and db.transaction use callbacks. This wraps in Promises | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  | for(var stmt of stmts) await new Promise((res, rej) => { | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  |   db.transaction(tx => | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  |     tx.executeSql(stmt, [], | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  |       (tx, data) => res(data), // if the query is successful, return the data | 
					
						
							|  |  |  |       (tx, err) => rej(err) // if the query fails, reject with the error | 
					
						
							|  |  |  |   )); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The result of a SQL SELECT statement is a `SQLResultSet`.  The `rows` property | 
					
						
							|  |  |  | is a `SQLResultSetRowList`.  It is an "array-like" structure that has `length` | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  | and properties like `0`, `1`, etc.  However, this is not a real Array object! | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | A real Array can be created using `Array.from`: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js | 
					
						
							|  |  |  | db.readTransaction(tx => | 
					
						
							|  |  |  |   tx.executeSQL("SELECT * FROM DatabaseTable", [], (tx, data) => { | 
					
						
							|  |  |  |     // data.rows is "array-like", so `Array.from` can make it a real array | 
					
						
							|  |  |  |     const aoo = Array.from(data.rows); | 
					
						
							|  |  |  |     const ws = XLSX.utils.json_to_sheet(aoo); | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  |     // ... perform an export here OR wrap in a Promise | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  |   }) | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Live Demo
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  | :::note | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This demo was last tested on 2023 February 26 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | The following demo generates a database with 5 fixed SQL statements. Queries | 
					
						
							|  |  |  | can be changed in the Live Editor.  The WebSQL database can be inspected in the | 
					
						
							|  |  |  | "WebSQL" section of the "Application" Tab of Developer Tools: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```jsx live | 
					
						
							|  |  |  | function SheetQL() { | 
					
						
							|  |  |  |   const [out, setOut] = React.useState(""); | 
					
						
							|  |  |  |   const queries = [ | 
					
						
							|  |  |  |     'DROP TABLE IF EXISTS Presidents', | 
					
						
							|  |  |  |     'CREATE TABLE Presidents (Name TEXT, Idx REAL)', | 
					
						
							|  |  |  |     'INSERT INTO Presidents  (Name, Idx) VALUES ("Barack Obama", 44)', | 
					
						
							|  |  |  |     'INSERT INTO Presidents  (Name, Idx) VALUES ("Donald Trump", 45)', | 
					
						
							|  |  |  |     'INSERT INTO Presidents  (Name, Idx) VALUES ("Joseph Biden", 46)' | 
					
						
							|  |  |  |   ]; | 
					
						
							|  |  |  |   const xport = React.useCallback(async() => { | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  |     /* prep database */ | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  |     const db = openDatabase('sheetql', '1.0', 'SheetJS WebSQL Test', 2097152); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  |     for(var q of queries) await new Promise((res, rej) => { | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  |       db.transaction((tx) => { | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  |         tx.executeSql(q, [], (tx, data) => res(data), (tx, err) => rej(err)); | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  |     /* pull data and generate rows */ | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  |     db.readTransaction(tx => { | 
					
						
							|  |  |  |       tx.executeSql("SELECT * FROM Presidents", [], (tx, data) => { | 
					
						
							|  |  |  |         const aoo = Array.from(data.rows); | 
					
						
							|  |  |  |         setOut("QUERY RESULT:\n" + aoo.map(r => JSON.stringify(r)).join("\n") + "\n") | 
					
						
							|  |  |  |         const ws = XLSX.utils.json_to_sheet(aoo); | 
					
						
							|  |  |  |         const wb = XLSX.utils.book_new(); | 
					
						
							|  |  |  |         XLSX.utils.book_append_sheet(wb, ws, "Presidents"); | 
					
						
							|  |  |  |         XLSX.writeFile(wb, "SheetQL.xlsx"); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   return ( <pre>{out}<button onClick={xport}><b>Fetch!</b></button></pre> ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Server-Side SQLite
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Most platforms offer a simple way to query SQLite database files. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The following example shows how to query for each table in an SQLite database, | 
					
						
							|  |  |  | query for the data for each table, add each non-empty table to a workbook, and | 
					
						
							|  |  |  | export as XLSX. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [The Northwind database is available in SQLite form](https://raw.githubusercontent.com/jpwhite3/northwind-SQLite3/master/dist/northwind.db). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  | :::note | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This demo was last tested on 2023 February 26 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | ### NodeJS
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The **`better-sqlite3`** module provides a very simple API for working with | 
					
						
							|  |  |  | SQLite databases. `Statement#all` runs a prepared statement and returns an array | 
					
						
							|  |  |  | of JS objects. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  | 0) [Download `northwind.db`](https://raw.githubusercontent.com/jpwhite3/northwind-SQLite3/master/dist/northwind.db). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | 1) Install the dependencies: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  | npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz better-sqlite3@8.1.0 | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 2) Save the following to `node.mjs`: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js title="node.mjs" | 
					
						
							|  |  |  | /* Load SQLite3 connector library */ | 
					
						
							|  |  |  | import Database from "better-sqlite3"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Load SheetJS library */ | 
					
						
							|  |  |  | import * as XLSX from 'xlsx'; | 
					
						
							|  |  |  | import * as fs from 'fs'; | 
					
						
							|  |  |  | XLSX.set_fs(fs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Initialize database */ | 
					
						
							|  |  |  | var db = Database("northwind.db"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Create new workbook */ | 
					
						
							|  |  |  | var wb = XLSX.utils.book_new(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Get list of table names */ | 
					
						
							|  |  |  | var sql = db.prepare("SELECT name FROM sqlite_master WHERE type='table'"); | 
					
						
							|  |  |  | var result = sql.all(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Loop across each name */ | 
					
						
							|  |  |  | result.forEach(function(row) { | 
					
						
							|  |  |  |   /* Get first 100K rows */ | 
					
						
							|  |  |  |   var aoo = db.prepare("SELECT * FROM '" + row.name + "' LIMIT 100000").all(); | 
					
						
							|  |  |  |   if(aoo.length > 0) { | 
					
						
							|  |  |  |     /* Create Worksheet from the row objects */ | 
					
						
							|  |  |  |     var ws = XLSX.utils.json_to_sheet(aoo, {dense: true}); | 
					
						
							|  |  |  |     /* Add to Workbook */ | 
					
						
							|  |  |  |     XLSX.utils.book_append_sheet(wb, ws, row.name); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Write File */ | 
					
						
							|  |  |  | XLSX.writeFile(wb, "node.xlsx"); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 3) Run `node node.mjs` and open `node.xlsx` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Bun
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Bun ships with a built-in high-performance module `bun:sqlite`. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  | 0) [Download `northwind.db`](https://raw.githubusercontent.com/jpwhite3/northwind-SQLite3/master/dist/northwind.db). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | 1) Install the dependencies: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							|  |  |  | npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 2) Save the following to `bun.mjs`: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js title="bun.mjs" | 
					
						
							|  |  |  | /* Load SQLite3 connector library */ | 
					
						
							|  |  |  | import { Database } from "bun:sqlite"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Load SheetJS library */ | 
					
						
							|  |  |  | import * as XLSX from 'xlsx'; | 
					
						
							|  |  |  | import * as fs from 'fs'; | 
					
						
							|  |  |  | XLSX.set_fs(fs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Initialize database */ | 
					
						
							|  |  |  | var db = Database.open("northwind.db"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Create new workbook */ | 
					
						
							|  |  |  | var wb = XLSX.utils.book_new(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Get list of table names */ | 
					
						
							|  |  |  | var sql = db.prepare("SELECT name FROM sqlite_master WHERE type='table'"); | 
					
						
							|  |  |  | var result = sql.all(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Loop across each name */ | 
					
						
							|  |  |  | result.forEach(function(row) { | 
					
						
							|  |  |  |   /* Get first 100K rows */ | 
					
						
							|  |  |  |   var aoo = db.prepare("SELECT * FROM '" + row.name + "' LIMIT 100000").all(); | 
					
						
							|  |  |  |   if(aoo.length > 0) { | 
					
						
							|  |  |  |     /* Create Worksheet from the row objects */ | 
					
						
							|  |  |  |     var ws = XLSX.utils.json_to_sheet(aoo, {dense: true}); | 
					
						
							|  |  |  |     /* Add to Workbook */ | 
					
						
							|  |  |  |     XLSX.utils.book_append_sheet(wb, ws, row.name); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Write File */ | 
					
						
							|  |  |  | XLSX.writeFile(wb, "bun.xlsx"); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 3) Run `bun bun.mjs` and open `bun.xlsx` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Deno
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Deno `sqlite` library returns raw arrays of arrays. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  | 0) [Download `northwind.db`](https://raw.githubusercontent.com/jpwhite3/northwind-SQLite3/master/dist/northwind.db). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | 1) Save the following to `deno.ts`: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```ts title="deno.ts" | 
					
						
							|  |  |  | /* Load SQLite3 connector library */ | 
					
						
							|  |  |  | import { DB } from "https://deno.land/x/sqlite/mod.ts"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Load SheetJS library */ | 
					
						
							|  |  |  | // @deno-types="https://cdn.sheetjs.com/xlsx-latest/package/types/index.d.ts" | 
					
						
							|  |  |  | import * as XLSX from 'https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Initialize database */ | 
					
						
							|  |  |  | var db = new DB("northwind.db"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Create new workbook */ | 
					
						
							|  |  |  | var wb = XLSX.utils.book_new(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Get list of table names */ | 
					
						
							|  |  |  | var sql = db.prepareQuery("SELECT name FROM sqlite_master WHERE type='table'"); | 
					
						
							|  |  |  | var result = sql.all(); | 
					
						
							|  |  |  | /* Loop across each name */ | 
					
						
							|  |  |  | result.forEach(function(row) { | 
					
						
							|  |  |  |   /* Get first 100K rows */ | 
					
						
							|  |  |  |   var query = db.prepareQuery("SELECT * FROM '" + row[0] + "' LIMIT 100000") | 
					
						
							|  |  |  |   var aoa = query.all(); | 
					
						
							|  |  |  |   if(aoa.length > 0) { | 
					
						
							|  |  |  |     /* Create array of arrays */ | 
					
						
							|  |  |  |     var data = [query.columns().map(x => x.name)].concat(aoa); | 
					
						
							|  |  |  |     /* Create Worksheet from the aoa */ | 
					
						
							|  |  |  |     var ws = XLSX.utils.aoa_to_sheet(data, {dense: true}); | 
					
						
							|  |  |  |     /* Add to Workbook */ | 
					
						
							|  |  |  |     XLSX.utils.book_append_sheet(wb, ws, row[0]); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Write File */ | 
					
						
							|  |  |  | XLSX.writeFile(wb, "deno.xlsx"); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-27 00:33:01 +00:00
										 |  |  | 2) Run `deno run --allow-read --allow-write deno.ts` and open `deno.xlsx` |