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.
Fundamentals
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.
Concepts
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.
Foundation
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]
);
});
Reference
📝 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.
Cheat Sheet
⚡ Quick Reference
Operation
Code pattern
All rows
SELECT * FROM users
Filter
SELECT * FROM users WHERE age > ?
One row by id
SELECT * FROM users WHERE id = ?
Row count
results.rows.length
Read row
results.rows.item(i)
Paginate
LIMIT ? OFFSET ?
Query
SELECT * FROM t
Read all
Filter
WHERE col = ?
Match rows
Loop
rows.item(i)
Each row
Empty
length === 0
No matches
Core Topic
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.
Results
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.
Reliability
Handling Errors
Errors occur for invalid SQL, missing tables, or database access failures. Zero rows returned is not an error—check rows.length separately.
Error callback — fires on SQL or schema errors; return true to roll back.
Empty result — rows.length === 0 means no matches; show a friendly empty state.
Null columns — missing values may be null; handle before displaying.
Hands-On
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);
}
});
});
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);
}
}
);
});
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 ChromeAll versions · Desktop & Mobile
Full support
Mozilla FirefoxAll versions · Desktop & Mobile
Full support
Apple SafariAll versions · macOS & iOS
Full support
Microsoft EdgeAll versions · Chromium & Legacy
Full support
Internet ExplorerIE 6+ · Legacy environments
Full support
OperaAll modern versions
Full support
Web SQL APIDeprecated
Bottom line: For cross-browser storage today, use IndexedDB.
Pro Tips
💡 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
Wrap Up
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.
Use these points when reading data from a client-side database.
5
Core concepts
🔍01
SELECT
Read rows.
Basics
📄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.