HTML Web SQL Retrieve

Beginner
⏱️ 11 min read
📚 Updated: Jul 2026
🎯 5 Examples + 3 Try It
SELECT / ResultSet / rows.item()

Introduction

Web SQL stores structured data in a local SQLite database. To read that data, you run SQL SELECT queries inside db.transaction and process the returned ResultSet. Although Web SQL is deprecated, knowing how to retrieve rows helps you maintain legacy apps.

This tutorial covers SELECT syntax, looping through results.rows, filtering with WHERE, and displaying data on a web page.

What You’ll Learn

01

SELECT

Read rows.

02

ResultSet

Query result.

03

rows.item()

Get one row.

04

WHERE

Filter data.

05

? bindings

Safe params.

06

Display

Show in UI.

What Is Web SQL?

Web SQL Database is a deprecated browser API for client-side relational storage. It uses SQLite, so you query data with familiar SQL—SELECT reads rows, INSERT adds them, UPDATE changes them, and DELETE removes them.

The W3C deprecated Web SQL in favor of IndexedDB. Chrome removed it in version 97. Use this page for learning and legacy maintenance.

Understanding Data Retrieval

Data retrieval means executing SELECT statements and processing the ResultSet returned in the success callback. The ResultSet has a rows collection—use rows.length for the count and rows.item(i) to read each row as a plain object.

💡
Beginner Tip

Think of SELECT as a question you ask the database: “Give me all users” or “Give me users older than 30.” The answer comes back as a list of row objects you loop through.

Setting Up the Database

Before retrieving data, open the database and ensure your table exists. Seed sample rows so your SELECT queries return something to display.

js
const db = openDatabase('myDatabase', '1.0', 'Test DB', 2 * 1024 * 1024);

db.transaction(function (tx) {
  tx.executeSql(
    'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)'
  );
  tx.executeSql(
    'INSERT OR IGNORE INTO users (id, name, age) VALUES (?, ?, ?)',
    [1, 'John Doe', 25]
  );
});

📝 SELECT Syntax

Standard SQL for reading data:

SQL
SELECT column1, column2 FROM table_name WHERE condition;
SELECT * FROM table_name ORDER BY column LIMIT 10 OFFSET 0;

In Web SQL, call SELECT through executeSql:

js
tx.executeSql(
  'SELECT * FROM users WHERE id = ?',
  [userId],
  function (tx, results) {
    for (var i = 0; i < results.rows.length; i++) {
      var row = results.rows.item(i);
      console.log(row.name, row.age);
    }
  },
  function (tx, error) { /* handle error */ }
);

ResultSet properties

  • results.rows.length — number of rows returned.
  • results.rows.item(i) — row object at index i (columns as properties).
  • results.insertId — meaningful for INSERT, not SELECT.
  • results.rowsAffected — meaningful for INSERT/UPDATE/DELETE, not SELECT.

⚡ Quick Reference

OperationCode pattern
All rowsSELECT * FROM users
FilterSELECT * FROM users WHERE age > ?
One row by idSELECT * FROM users WHERE id = ?
Row countresults.rows.length
Read rowresults.rows.item(i)
PaginateLIMIT ? OFFSET ?
Query
SELECT * FROM t

Read all

Filter
WHERE col = ?

Match rows

Loop
rows.item(i)

Each row

Empty
length === 0

No matches

Executing SELECT Queries

Retrieve all records from a table and log each name to the console:

js
db.transaction(function (tx) {
  tx.executeSql(
    'SELECT * FROM users',
    [],
    function (tx, results) {
      for (var i = 0; i < results.rows.length; i++) {
        console.log(results.rows.item(i).name);
      }
    },
    function (tx, error) {
      console.error('Query error:', error.message);
      return true;
    }
  );
});

The empty array [] is the bindings list—no ? placeholders needed for SELECT * without a WHERE clause.

Processing Query Results

Filter with WHERE and read column values from each row object:

js
db.transaction(function (tx) {
  tx.executeSql(
    'SELECT * FROM users WHERE age > ?',
    [30],
    function (tx, results) {
      for (var i = 0; i < results.rows.length; i++) {
        var row = results.rows.item(i);
        console.log('ID:', row.id, 'Name:', row.name, 'Age:', row.age);
      }
    }
  );
});

Only users with age > 30 are returned. You can also access rows with results.rows[i] in some browsers, but item(i) is the portable approach.

Handling Errors

Errors occur for invalid SQL, missing tables, or database access failures. Zero rows returned is not an error—check rows.length separately.

js
db.transaction(function (tx) {
  tx.executeSql(
    'SELECT * FROM nonExistentTable',
    [],
    null,
    function (tx, error) {
      console.error('Error occurred:', error.message);
      return true;
    }
  );
});
  • Error callback — fires on SQL or schema errors; return true to roll back.
  • Empty resultrows.length === 0 means no matches; show a friendly empty state.
  • Null columns — missing values may be null; handle before displaying.

Examples Gallery

These examples use the Web SQL API pattern. Use the Try It Yourself links to run live demos. Web SQL works only in browsers that still expose openDatabase; otherwise the demo shows a clear status message.

📚 Getting Started

Run a basic SELECT and loop through the ResultSet.

Example 1 — SELECT All Rows

Fetch every user and log their names.

js
db.transaction(function (tx) {
  tx.executeSql('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)');

  tx.executeSql('SELECT * FROM users', [], function (tx, results) {
    var len = results.rows.length;
    for (var i = 0; i < len; i++) {
      console.log(results.rows.item(i).name);
    }
  });
});
Try It Yourself

How It Works

SELECT * returns all columns for every row. The loop reads each row’s name property.

Example 2 — SELECT with WHERE

Filter users older than 30 with a bound parameter.

js
db.transaction(function (tx) {
  tx.executeSql(
    'SELECT * FROM users WHERE age > ? ORDER BY age',
    [30],
    function (tx, results) {
      for (var i = 0; i < results.rows.length; i++) {
        var row = results.rows.item(i);
        console.log(row.name + ' — age ' + row.age);
      }
    }
  );
});
Try It Yourself

How It Works

WHERE age > ? with binding [30] keeps the query safe. ORDER BY age sorts results before you display them.

📈 Practical Patterns

Fetch one row, paginate results, and render data in HTML.

Example 3 — Fetch One Row by ID

Retrieve a single user when you know the primary key.

js
function getUserById(id, callback) {
  db.transaction(function (tx) {
    tx.executeSql(
      'SELECT * FROM users WHERE id = ?',
      [id],
      function (tx, results) {
        if (results.rows.length === 0) {
          callback(null);
        } else {
          callback(results.rows.item(0));
        }
      }
    );
  });
}

getUserById(2, function (user) {
  if (user) console.log(user.name, user.age);
  else console.log('User not found');
});
Try It Yourself

How It Works

rows.item(0) is the first (and only) matching row when id is unique. Return null when length is zero.

Example 4 — Paginate with LIMIT and OFFSET

Load ten rows at a time for large tables.

js
var pageSize = 10;
var page = 0; /* 0-based page index */

db.transaction(function (tx) {
  tx.executeSql(
    'SELECT * FROM users ORDER BY id LIMIT ? OFFSET ?',
    [pageSize, page * pageSize],
    function (tx, results) {
      console.log('Page', page, '—', results.rows.length, 'rows');
    }
  );
});
Try It Yourself

How It Works

LIMIT caps how many rows return; OFFSET skips earlier pages. Increment page for “Load more” buttons.

Example 5 — Complete HTML Page

Retrieve all users and render them in a list on the page.

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Web SQL Retrieve Example</title>
</head>
<body>
  <h1>Data from users table</h1>
  <ul id="dataList"></ul>
  <p id="status"></p>

  <script>
    if (!window.openDatabase) {
      document.getElementById('status').textContent =
        'Web SQL is not supported in this browser.';
    } else {
      const db = openDatabase('myDatabase', '1.0', 'Test DB', 2 * 1024 * 1024);

      db.transaction(function (tx) {
        tx.executeSql('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)');
        tx.executeSql('INSERT OR IGNORE INTO users (id, name, age) VALUES (?, ?, ?)', [1, 'John Doe', 25]);
        tx.executeSql('INSERT OR IGNORE INTO users (id, name, age) VALUES (?, ?, ?)', [2, 'Jane Smith', 32]);
      });

      db.transaction(function (tx) {
        tx.executeSql('SELECT * FROM users', [], function (tx, results) {
          const list = document.getElementById('dataList');
          for (let i = 0; i < results.rows.length; i++) {
            const row = results.rows.item(i);
            const li = document.createElement('li');
            li.textContent = 'ID: ' + row.id + ', Name: ' + row.name + ', Age: ' + row.age;
            list.appendChild(li);
          }
          document.getElementById('status').textContent =
            'Loaded ' + results.rows.length + ' row(s).';
        }, function (tx, error) {
          document.getElementById('status').textContent = 'Error: ' + error.message;
        });
      });
    }
  </script>
</body>
</html>
Try It Yourself

How It Works

The page seeds data, runs SELECT *, and builds <li> elements from each row. Status text confirms how many rows loaded.

🚀 Common Use Cases

  • Offline reading lists — display saved articles or notes from local storage.
  • Cart display — SELECT cart items and render product names and prices.
  • Search filters — WHERE clauses match user search terms (with bound parameters).
  • Settings screens — read preference rows and populate form fields.
  • Legacy app maintenance — debug data in older Web SQL databases.

🧠 How Web SQL SELECT Works

1

Run SELECT

executeSql sends the query to SQLite.

Query
2

Get ResultSet

Success callback receives results.rows.

Result
3

Loop rows

rows.item(i) gives each row object.

Read
=

Display data

Render rows in the DOM, console, or app state.

📝 Notes

  • Web SQL is deprecated—use IndexedDB for new apps.
  • SELECT is read-only; it does not change data.
  • Zero rows is success, not failure—handle empty lists in the UI.
  • Always bind dynamic values in WHERE with ?.
  • Use LIMIT for large tables to avoid loading everything at once.
  • Pair with UPDATE and DELETE for full CRUD.

Browser Support

Web SQL was never implemented in Firefox. Chrome removed it in Chrome 97. Safari historically supported it on some versions. Feature-detect with window.openDatabase before running demos.

Baseline · Since HTML

Web SQL API

Web SQL was never implemented in Firefox. Chrome removed it in Chrome 97. Safari historically supported it on some versions. Feature-detect with window.openDatabase before running demos.

25% Modern browser support
Google Chrome All versions · Desktop & Mobile
Full support
Mozilla Firefox All versions · Desktop & Mobile
Full support
Apple Safari All versions · macOS & iOS
Full support
Microsoft Edge All versions · Chromium & Legacy
Full support
Internet Explorer IE 6+ · Legacy environments
Full support
Opera All modern versions
Full support
Web SQL API Deprecated

Bottom line: For cross-browser storage today, use IndexedDB.

💡 Best Practices

✅ Do

  • Use parameterized WHERE clauses
  • Check rows.length for empty results
  • Use LIMIT and OFFSET for pagination
  • Select only columns you need (SELECT id, name)
  • Handle error callbacks on every query
  • Prefer IndexedDB for new projects

❌ Don’t

  • Concatenate user input into SQL strings
  • Assume SELECT * on huge tables is fast
  • Treat zero rows as a SQL error
  • Forget to handle null column values
  • Run SELECT outside a transaction
  • Build new features on Web SQL in 2026

Conclusion

Retrieving data with Web SQL means running SELECT inside db.transaction, looping results.rows, and reading each row with rows.item(i). Filter with WHERE, bind parameters safely, and handle empty results gracefully.

While Web SQL is deprecated, SELECT skills transfer to any SQL database. For modern browsers, migrate reads to IndexedDB—but the query thinking you practiced here stays valuable.

Key Takeaways

Knowledge Unlocked

Five things to remember about Web SQL SELECT

Use these points when reading data from a client-side database.

5
Core concepts
📄 02

ResultSet

results.rows

Structure
🔄 03

rows.item()

Each row.

Loop
🎯 04

WHERE ?

Filter safe.

Query
🔄 05

Empty OK

length === 0

UI

❓ 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 results.rows and read each row with results.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.age).
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.
results.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.
Did you know?

In Web SQL, the rows object is array-like but not a true JavaScript array. That is why the API provides item(index) instead of expecting you to use array methods like forEach directly on rows.

Practice Web SQL SELECT

Click Load Data in the Try It editor and see rows appear in a list on the page.

Open Try It editor →

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.

6 people found this page helpful