The only performance write-up I've seen is the one produced by @Scott (author of the other answer to this question). Unfortunately, his article doesn't do Web SQL Database justice, since it uses an inefficient HAVING clause to restrict the size of the result set. I tweaked Scott's SQL, replacing HAVING, GROUP BY and LEFT JOIN with (almost) equivalent WHERE and sub-selects:
SELECT p.Name AS ProgramName,
s.rowid,
s.Name,
s.NowShowing,
s.ProgramID,
(SELECT COUNT(*) FROM Episode WHERE SeriesID = s.rowid AND STATUS IN ('Watched', 'Recorded', 'Expected') OR STATUS IS NULL) AS EpisodeCount,
(SELECT COUNT(*) FROM Episode WHERE SeriesID = s.rowid AND STATUS = 'Watched') AS WatchedCount,
(SELECT COUNT(*) FROM Episode WHERE SeriesID = s.rowid AND STATUS = 'Recorded') AS RecordedCount,
(SELECT COUNT(*) FROM Episode WHERE SeriesID = s.rowid AND STATUS = 'Expected') AS ExpectedCount
FROM Program p
JOIN Series s ON p.rowid = s.ProgramID
WHERE s.NowShowing IS NOT NULL OR
EXISTS (SELECT * FROM Episode WHERE SeriesID = s.rowid AND STATUS IN ('Recorded', 'Expected'))
ORDER BY CASE
WHEN s.NowShowing IS NULL THEN 1
ELSE 0
END,
s.NowShowing,
p.Name
This is about 28 times faster than the original — 20 ms vs 560 ms on my computer — which, by extrapolation from Scott's numbers, makes it about 10 times faster than the equivalent IndexedDB. I wasn't able to confirm this because the IndexedDB code doesn't work in my browser, seemingly due to API changes.
I should explain the "(almost)" I wrote above. Scott's original SQL and mine have subtly different meanings: a gratuitous WHERE clause on my EpisodeCount — which has the effect of replacing a table scan with an index search — may fail to count some episodes if it doesn't cover all possible Status values. Removing this clause erases the difference at the expense of doubling execution time to 40 ms.
Note that, earlier, I discussed with Scott a smaller change to his SQL that also achieves a 40 ms time.
UPDATE: Many thanks to Scott for updating his article to acknowledge the discussion we had.