HTML Web SQL Retrieve

Beginner
8 min read
Updated: Nov 2026
5 examples
Live preview

Definition and Usage

Web SQL lets you read rows with a normal SQL SELECT statement, run through tx.executeSql() inside a transaction. In the success callback, loop result.rows with rows.item(i) to get each row as an object.

Pair this with INSERT to add data first, and UPDATE / DELETE after you find the right rows. For new apps, prefer IndexedDB retrieve.

Deprecated API.

Web SQL is obsolete and removed from modern Chrome. Use this page to maintain legacy code or learn SQL selects. New projects should use IndexedDB.

Related ideas.

rows is array-like, not a real array—use rows.item(i) and rows.length. Filter with WHERE, sort with ORDER BY, and page with LIMIT / OFFSET.

Syntax

syntax.sql
SELECT columns FROM table_name WHERE condition

In Web SQL, wrap that statement in executeSql with bound arguments:

JS
tx.executeSql(
  'SELECT id, name FROM items WHERE id = ?',
  [id],
  function (tx, result) { /* success */ },
  function (tx, error)  { /* failure */ }
);

Parameters

The executeSql call for a select takes these arguments:

ArgumentTypeDescription
sqlStatementStringThe SELECT ... FROM ... text, with ? placeholders for dynamic values.
argumentsArrayValues bound to each ? in order—e.g. [id] or [minAge].
successCallbackFunction (optional)Called as (tx, result). Loop result.rows with item(i).
errorCallbackFunction (optional)Called as (tx, error). Return true to roll back the transaction.

Prefer listing columns over SELECT * when you only need a few fields. Bind filter values with ?—never paste user input into the SQL string.

Return Value

executeSql does not return a useful value itself. On success, your callback receives (tx, result). For SELECT, use result.rows to read the matched data.

PropertyTypeMeaning after SELECT
result.rowsSQLResultSetRowListThe matched rows (array-like; use item(i))
result.rows.lengthNumberHow many rows matched
result.rows.item(i)ObjectOne row; column names are properties (e.g. row.name)
result.rowsAffectedNumberUsually 0 for SELECT (no rows changed)
result.insertIdNumberNot useful for SELECT (used after INSERT)

rows.length values:

ValueWhat it means
0Query succeeded but nothing matched. Not an error—show an empty state.
1 or moreThat many rows are available via rows.item(0) … rows.item(n - 1).

How It Works

A typical read follows these steps:

  1. Open the database with openDatabase(name, version, displayName, size).
  2. Start a transaction: db.transaction(function (tx) { ... }) (or readTransaction for reads only).
  3. Run SELECT via tx.executeSql(sql, args, success, error).
  4. Loop result.rows with item(i) in the success callback.
  5. Handle empty results when rows.length === 0.
JS
db.transaction(function (tx) {
  tx.executeSql(
    'SELECT id, name, email FROM contacts ORDER BY id',
    [],
    function (tx, result) {
      for (var i = 0; i < result.rows.length; i++) {
        var row = result.rows.item(i);
        console.log(row.id, row.name, row.email);
      }
    },
    function (tx, error) {
      console.error(error.message);
      return true;
    }
  );
});

Live Preview

Seed sample contacts, then select all, filter by id, or clear. Status updates below. Many modern browsers no longer support Web SQL—if so, the status box will say so.

Uses a private demo database when Web SQL is available. Prefer IndexedDB for new work.

Status

Checking Web SQL support…

Examples

Short, copy-ready snippets. Use Try It Yourself for a live editor. Demos need a browser that still exposes openDatabase.

Example 1 — SELECT All Rows

Read every contact and loop with rows.item(i).

JS
db.transaction(function (tx) {
  tx.executeSql(
    'SELECT id, name, email FROM contacts',
    [],
    function (tx, result) {
      console.log('Found', result.rows.length, 'row(s)');
      for (var i = 0; i < result.rows.length; i++) {
        var row = result.rows.item(i);
        console.log(row.id, row.name);
      }
    }
  );
});
Try It Yourself

How It Works

  1. Run SELECT with an empty args array when there are no ? placeholders.
  2. Check rows.length for the count.
  3. Loop with item(i) to read each row object.

Example 2 — SELECT with WHERE

Fetch one row by primary key using a bound parameter.

JS
db.transaction(function (tx) {
  tx.executeSql(
    'SELECT id, name, email FROM contacts WHERE id = ?',
    [1],
    function (tx, result) {
      if (result.rows.length === 0) {
        console.warn('No contact with id 1');
        return;
      }
      var row = result.rows.item(0);
      console.log(row.name, row.email);
    }
  );
});
Try It Yourself

How It Works

  1. Bind the id with ? and [1].
  2. Guard empty results before calling item(0).
  3. Read column properties from the row object.

Example 3 — Empty Results

Zero rows is success, not an error. Handle it in the UI.

JS
function findContact(db, id) {
  db.transaction(function (tx) {
    tx.executeSql(
      'SELECT name FROM contacts WHERE id = ?',
      [id],
      function (tx, result) {
        if (result.rows.length === 0) {
          console.log('Not found');
        } else {
          console.log('Found:', result.rows.item(0).name);
        }
      }
    );
  });
}

findContact(db, 99);
Try It Yourself

How It Works

  1. Run the SELECT for an id that may not exist.
  2. Check rows.length === 0 in the success callback.
  3. Show “not found” instead of treating it as a failure.

Example 4 — ORDER BY + LIMIT

Sort and take only the first few rows for a simple list page.

JS
db.transaction(function (tx) {
  tx.executeSql(
    'SELECT id, name FROM contacts ORDER BY name ASC LIMIT ? OFFSET ?',
    [2, 0],
    function (tx, result) {
      for (var i = 0; i < result.rows.length; i++) {
        console.log(result.rows.item(i).name);
      }
    }
  );
});
Try It Yourself

How It Works

  1. Sort with ORDER BY so the page order is stable.
  2. Bind LIMIT and OFFSET for pagination.
  3. Loop the page of rows in the success callback.

Example 5 — Complete HTML Page

Seed rows on load, then list them from a button click.

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Web SQL Retrieve Demo</title>
</head>
<body>
  <h1>Web SQL SELECT</h1>
  <button type="button" id="listBtn">List contacts</button>
  <p id="status">Opening…</p>

  <script>
    var statusEl = document.getElementById('status');
    if (!window.openDatabase) {
      statusEl.textContent = 'Web SQL is not available in this browser.';
    } else {
      var db = openDatabase('retrieveDemoDb', '1.0', 'Demo', 1024 * 1024);
      db.transaction(function (tx) {
        tx.executeSql(
          'CREATE TABLE IF NOT EXISTS contacts (id INTEGER PRIMARY KEY, name TEXT, email TEXT)'
        );
        tx.executeSql('DELETE FROM contacts');
        tx.executeSql(
          'INSERT INTO contacts (name, email) VALUES (?, ?)',
          ['Alex Rivera', '[email protected]']
        );
        tx.executeSql(
          'INSERT INTO contacts (name, email) VALUES (?, ?)',
          ['Sam Lee', '[email protected]']
        );
      }, null, function () {
        statusEl.textContent = 'Ready. Click List.';
      });

      document.getElementById('listBtn').addEventListener('click', function () {
        db.transaction(function (tx) {
          tx.executeSql(
            'SELECT id, name FROM contacts ORDER BY id',
            [],
            function (tx, result) {
              var parts = [];
              for (var i = 0; i < result.rows.length; i++) {
                var row = result.rows.item(i);
                parts.push('#' + row.id + ' ' + row.name);
              }
              statusEl.textContent = parts.join(', ') || 'No rows.';
            }
          );
        });
      });
    }
  </script>
</body>
</html>
Try It Yourself

How It Works

  1. Feature-detect openDatabase before running any SQL.
  2. Create and seed the table on load.
  3. On button click, run SELECT ... ORDER BY id.
  4. Build status text from each rows.item(i).

Edge Cases & Pitfalls

Most SELECT bugs come from treating rows like a real array or ignoring empty results.

rows

Use item(i), not [i]

result.rows is array-like. Prefer rows.item(i) and rows.length—do not assume forEach works.

empty

Zero rows is not an error

Check rows.length === 0 before calling item(0), or you risk reading undefined.

inject

Bind with ?

Never concatenate user input into the SQL string. Pass filter values in the arguments array.

tx

Must use a transaction

All executeSql calls belong inside db.transaction() or readTransaction().

legacy

API may be missing

Feature-detect window.openDatabase. Modern Chrome has no Web SQL—point users to IndexedDB.

Browser Support

Web SQL is a deprecated client-side API. Firefox never shipped it. Chrome removed it (about Chrome 97+). Detect with window.openDatabase before running demos.

✓ Deprecated

Web SQL SELECT

Educational / legacy only. Prefer IndexedDB get(), getAll(), and openCursor() for new production apps.

Legacy Modern browser support
Google Chrome Removed ~97+
Removed
Mozilla Firefox —
Never supported
Apple Safari Legacy
Historical / limited
Microsoft Edge Removed
Removed (Chromium)
Opera Removed
Removed (Chromium)
Samsung Internet Legacy
Do not rely on
Internet Explorer —
Not supported
Web SQL SELECT Deprecated

Bottom line: For reads in new web apps, use IndexedDB retrieve. Keep Web SQL only for maintaining old codebases.

Key Takeaways

  • Read rows: SELECT ... FROM t inside db.transaction().
  • Access data: loop result.rows with item(i) and length.
  • Filter safely: use WHERE ... ? with a bound args array.
  • Legacy: Web SQL is deprecated—use IndexedDB for new projects.

One line: select with SELECT ... FROM t via executeSql, loop rows.item(i), prefer IndexedDB going forward.

Frequently Asked Questions

Open the database with openDatabase(), then call db.transaction(). Inside the transaction, use tx.executeSql() with a SELECT statement. In the success callback, loop result.rows and read each row with result.rows.item(i).
Standard SQL: SELECT column1, column2 FROM table_name WHERE condition. Use SELECT * to fetch all columns. Add ORDER BY, LIMIT, and OFFSET for sorting and pagination.
The success callback receives a result object. result.rows.length is the row count. result.rows.item(index) returns one row as an object with column names as properties (e.g. row.name, row.email).
Yes. Use ? placeholders and pass values in the bindings array: SELECT * FROM users WHERE age > ? with [30]. This prevents SQL injection.
Web SQL is deprecated and removed from most modern browsers (including current Chrome). Treat this tutorial as educational for legacy code; use IndexedDB for new projects.
result.rows.length will be 0. That is not an error—the query succeeded but found no matches. Show an empty state in your UI instead of treating it as a failure.

Next: Web SQL UPDATE

Change existing rows with UPDATE after you select them.

Update tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

10 people found this page helpful