docs.sheetjs.com/docz/docs/03-demos/07-data/01-websql.md
2023-10-12 04:39:38 -04:00

7.7 KiB

title pagination_prev pagination_next sidebar_custom_props
WebSQL and SQLite demos/desktop/index demos/local/index
type sql
web true

import current from '/version.js'; import CodeBlock from '@theme/CodeBlock';

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.

:::caution pass

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.

:::

WebSQL Details

Importing data from spreadsheets is straightforward using the generate_sql helper function from "Generating Tables".

The Web SQL Database API is callback-based. The following snippet wraps transactions in Promise objects:

const db = openDatabase('sheetql', '1.0', 'SheetJS WebSQL Test', 2097152);
const stmts = generate_sql(ws, wsname);

// NOTE: tx.executeSql and db.transaction use callbacks. This wraps in Promises
for(var stmt of stmts) await new Promise((res, rej) => {
  db.transaction(tx =>
    tx.executeSql(stmt, [],
      (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 and properties like 0, 1, etc. However, this is not a real Array object!

A real Array can be created using Array.from:

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);
    // ... perform an export here OR wrap in a Promise
  })
);

Live Demo

:::note

This browser demo was tested in the following environments:

Browser Date
Chrome 117 2023-10-11

Some lesser-used browsers do not support WebSQL:

Browser Date Support
Safari 17.0 2023-10-11 Error Web SQL is deprecated
Firefox 118 2023-10-11 Error openDatabase is not defined

:::

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

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() => {
    /* prep database */
    const db = openDatabase('sheetql', '1.0', 'SheetJS WebSQL Test', 2097152);

    for(var q of queries) await new Promise((res, rej) => {
      db.transaction((tx) => {
        tx.executeSql(q, [], (tx, data) => res(data), (tx, err) => rej(err));
      });
    });

    /* pull data and generate rows */
    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 Chinook database is a MIT-licensed sample database. The original source code repository http://chinookdatabase.codeplex.com is no longer available, so the raw SQL queries are mirrored here.

:::note

This demo was last tested on 2023 May 28

:::

NodeJS

The better-sqlite3 module provides an API for working with SQLite databases. Statement#all runs a prepared statement and returns an array of objects:

import Database from "better-sqlite3";
import * as XLSX from "xlsx";

/* open database */
var db = Database("chinook.db");

/* get data from the `Invoice` table */
var aoo = db.prepare("SELECT * FROM 'Invoice' LIMIT 100000").all();

/* create worksheet from the row objects */
var ws = XLSX.utils.json_to_sheet(aoo, {dense: true});
  1. Build chinook.db from the SQL statements:
curl -LO https://docs.sheetjs.com/sqlite/chinook.sql
sqlite3 chinook.db ".read chinook.sql"
  1. Install the dependencies:

{\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz better-sqlite3@8.1.0}

  1. Download SheetJSQLiteNode.mjs:
curl -LO https://docs.sheetjs.com/sqlite/SheetJSQLiteNode.mjs
  1. Run node SheetJSQLiteNode.mjs and open SheetJSQLiteNode.xlsx

Bun

Bun ships with a built-in high-performance module bun:sqlite:

import { Database } from "bun:sqlite";
import * as XLSX from "xlsx";

/* open database */
var db = Database.open("chinook.db");

/* get data from the `Invoice` table */
var aoo = db.prepare("SELECT * FROM 'Invoice' LIMIT 100000").all();

/* create worksheet from the row objects */
var ws = XLSX.utils.json_to_sheet(aoo, {dense: true});
  1. Build chinook.db from the SQL statements:
curl -LO https://docs.sheetjs.com/sqlite/chinook.sql
sqlite3 chinook.db ".read chinook.sql"
  1. Install the dependencies:

{\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz}

  1. Download SheetJSQLiteBun.mjs:
curl -LO https://docs.sheetjs.com/sqlite/SheetJSQLiteBun.mjs
  1. Run bun run SheetJSQLiteBun.mjs and open SheetJSQLiteBun.xlsx

Deno

Deno sqlite library returns raw arrays of arrays:

{\ import { DB } from "https://deno.land/x/sqlite/mod.ts"; // @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\ /* open database */ var db = new DB("chinook.db"); \n\ /* get data from the \Invoice` table / var aoa = db.prepareQuery("SELECT * FROM 'Invoice' LIMIT 100000").all(); \n
/
create worksheet from the row objects */ var data = [query.columns().map(x => x.name)].concat(aoa); var ws = XLSX.utils.aoa_to_sheet(data, {dense: true});`}

  1. Build chinook.db from the SQL statements:
curl -LO https://docs.sheetjs.com/sqlite/chinook.sql
sqlite3 chinook.db ".read chinook.sql"
  1. Download SheetJSQLiteDeno.ts:
curl -LO https://docs.sheetjs.com/sqlite/SheetJSQLiteDeno.ts
  1. Run deno run --allow-read --allow-write SheetJSQLiteDeno.ts and open SheetJSQLiteDeno.xlsx