2023-02-24 07:46:48 +00:00
|
|
|
---
|
|
|
|
title: WebSQL and SQLite
|
2023-02-28 11:40:44 +00:00
|
|
|
pagination_prev: demos/desktop/index
|
|
|
|
pagination_next: demos/local/index
|
2023-02-24 07:46:48 +00:00
|
|
|
sidebar_custom_props:
|
|
|
|
type: web
|
|
|
|
sql: true
|
|
|
|
---
|
|
|
|
|
2023-04-27 09:12:19 +00:00
|
|
|
import current from '/version.js';
|
2023-05-01 01:27:02 +00:00
|
|
|
import CodeBlock from '@theme/CodeBlock';
|
2023-04-27 09:12:19 +00:00
|
|
|
|
2023-02-24 07:46:48 +00:00
|
|
|
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.
|
|
|
|
|
2023-03-12 06:25:57 +00:00
|
|
|
:::caution
|
|
|
|
|
|
|
|
WebSQL is only supported in Chromium-based browsers including Chrome.
|
|
|
|
|
|
|
|
Safari historically supported WebSQL but Safari 13 dropped support. Legacy
|
|
|
|
browsers including Internet Explorer and Firefox never added support.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
2023-02-24 07:46:48 +00:00
|
|
|
## 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
|
|
|
|
|
2023-05-01 01:27:02 +00:00
|
|
|
This demo was last tested on 2023 April 30
|
2023-02-27 00:33:01 +00:00
|
|
|
|
|
|
|
:::
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
![WebSQL view in Developer Tools](pathname:///files/websql.png)
|
|
|
|
|
|
|
|
```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:
|
|
|
|
|
2023-05-01 01:27:02 +00:00
|
|
|
<CodeBlock language="bash">{`\
|
|
|
|
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz better-sqlite3@8.1.0`}
|
|
|
|
</CodeBlock>
|
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:
|
|
|
|
|
2023-05-01 01:27:02 +00:00
|
|
|
<CodeBlock language="bash">{`\
|
|
|
|
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
|
|
|
</CodeBlock>
|
2023-02-24 07:46:48 +00:00
|
|
|
|
|
|
|
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`:
|
|
|
|
|
2023-05-01 01:27:02 +00:00
|
|
|
<CodeBlock language="ts" title="deno.ts">{`\
|
2023-02-24 07:46:48 +00:00
|
|
|
/* Load SQLite3 connector library */
|
|
|
|
import { DB } from "https://deno.land/x/sqlite/mod.ts";
|
2023-05-01 01:27:02 +00:00
|
|
|
\n\
|
2023-02-24 07:46:48 +00:00
|
|
|
/* Load SheetJS library */
|
2023-05-01 01:27:02 +00:00
|
|
|
// @deno-types="https://cdn.sheetjs.com/xlsx-${current}/package/types/index.d.ts"
|
|
|
|
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs';
|
|
|
|
\n\
|
2023-02-24 07:46:48 +00:00
|
|
|
/* Initialize database */
|
|
|
|
var db = new DB("northwind.db");
|
2023-05-01 01:27:02 +00:00
|
|
|
\n\
|
2023-02-24 07:46:48 +00:00
|
|
|
/* Create new workbook */
|
|
|
|
var wb = XLSX.utils.book_new();
|
2023-05-01 01:27:02 +00:00
|
|
|
\n\
|
2023-02-24 07:46:48 +00:00
|
|
|
/* 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]);
|
|
|
|
}
|
|
|
|
});
|
2023-05-01 01:27:02 +00:00
|
|
|
\n\
|
2023-02-24 07:46:48 +00:00
|
|
|
/* Write File */
|
2023-05-01 01:27:02 +00:00
|
|
|
XLSX.writeFile(wb, "deno.xlsx");`}
|
|
|
|
</CodeBlock>
|
2023-02-24 07:46:48 +00:00
|
|
|
|
2023-02-27 00:33:01 +00:00
|
|
|
2) Run `deno run --allow-read --allow-write deno.ts` and open `deno.xlsx`
|