QuestDB 9.3.0 is now available, bringing a new wave of query expressiveness and usability improvements across the engine and the Web Console. This release introduces window joins for precise time-based analytics, database views for cleaner query composition, AI-assisted workflows in the web console, and the new PIVOT keyword for effortless wide-schema aggregations.
For any questions or feedback, please join us on Slack or on Discourse.
Views are virtual tables defined by a SELECT statement. They do not persist data on disk. Instead, the underlying query is executed as a subquery each time the view is referenced.
Complex calculations and logic can be captured in a view, and shared across database users.
CREATE VIEW latest_trades AS
SELECT
symbol,
side,
last(price) AS price,
last(timestamp) AS ts
FROM trades
GROUP BY symbol, side;
Views can be queried just like tables, while always reflecting the latest data from their source tables.
Parameterisation
Views can be parameterised:
CREATE VIEW filtered_trades AS (
DECLARE
OVERRIDABLE @min_price := 100
SELECT ts, symbol, price
FROM trades
WHERE price >= @min_price
);
This view contains the @min_price parameter, with a default value. Additionally, its tagged as OVERRIDABLE. This means that the user reading from the View can alter this parameter:
DECLARE
@min_price := 500
SELECT * FROM filtered_trades
Within a View, DECLARE variables are constant unless explicitly declared to be OVERRIDABLE. This allows you to create Views with pre-set filters, limiting which rows the end user has access to:
CREATE VIEW last_24h_trades AS (
DECLARE
@min_ts := dateadd('d', -1, now())
SELECT * FROM trades
WHERE timestamp >= @min_ts
)
The end user cannot widen this lower time bounds parameter.
With PIVOT, the same aggregate can be computed and output with a column generated for each pivot key combination. In this case, the pivoting key is symbol, and the aggregate value is last(price).
trades_latest_1d
PIVOT (
last(price)
FOR symbol IN ('BTC-USD', 'ETH-USD')
GROUP BY side
) ORDER BY side;
QuestDB now supports WINDOW JOIN, a new join syntax designed specifically for time-based analytics.
WINDOW JOIN allows each row from a primary table to be joined with a time window of rows from another table, with aggregations computed over the matching rows. This makes short-horizon analytics—such as correlating trades with nearby market prices—both expressive and efficient.
SELECT
t.*,
avg(p.bid) AS avg_bid,
avg(p.ask) AS avg_ask
FROM trades t
WINDOW JOIN prices p
ON p.sym = t.sym
RANGE BETWEEN 1 second PRECEDING
AND 1 second FOLLOWING
INCLUDE PREVAILING;
In this example, each row from trades is joined with all rows from prices that share the same symbol and fall within the [-1s, +1s] interval (inclusive). Aggregations are then calculated over the joined rows.
This syntax avoids complex subqueries and makes time-windowed joins explicit, readable, and performant, with SIMD acceleration employed for the most common aggregate functions.
The QuestDB Web Console now includes LLM-powered assistance, available on an opt-in basis using your own API key.
Once enabled, the console can:
Auto-describe table schemas and column semantics
Annotate sample rows with plain-language context
Walk through query execution plans, highlighting joins, filters, and index usage
This makes it easier to understand both what a query does and how it executes—directly where you write SQL.
Performance
JIT-compiled filters have undergone an optimisation pass, and will now execute up to 2x faster.
Queries with GROUP BY, ORDER BY and LIMIT can now be executed in parallel, with execution times up to 10x faster.
SELECT min(timestamp), max(timestamp) FROM table will now execute in O(1) time (instantly), when timestamp is the designated timestamp.
Querying external parquet files with read_parquet('data.parquet') will now use projection pushdown, signficantly reducing disk and memory usage, and speeding up execution times by as much as 100x.
Parquet exporting has been sped up by reducing the number of intermediate commits applied during the copy process.
Changelist
fix(sql): segfault when using touch() on a table with a new empty column by @nwoolmer in https://github.com/questdb/questdb/pull/6561
feat(sql): window join by @puzpuzpuz in https://github.com/questdb/questdb/pull/6292
fix(pgwire): add support for SHOW default_transaction_read_only by @nwoolmer in https://github.com/questdb/questdb/pull/6562
fix(core): fix incorrect designated timestamp column displayed by tables() after column type changes by @glasstiger in https://github.com/questdb/questdb/pull/6570
fix(sql): support copies of very wide tables and result sets by @nwoolmer in https://github.com/questdb/questdb/pull/6525
fix(sql): fix false timestamp ordering error with nested SAMPLE BY and ORDER BY by @bluestreak01 in https://github.com/questdb/questdb/pull/6572
perf(sql): avoid expensive row counting in EXPLAIN query with LIMIT by @mtopolnik in https://github.com/questdb/questdb/pull/6540
feat(sql): align glob() function with DuckDB glob syntax by @kafka1991 in https://github.com/questdb/questdb/pull/6552
perf(sql): speed up JIT-compiled filters by reordering predicates and short-circuiting them by @puzpuzpuz in https://github.com/questdb/questdb/pull/6568
feat(core): release disk space faster on table drop by @ideoma in https://github.com/questdb/questdb/pull/6555
perf(sql): speed up row counting in filter queries by @puzpuzpuz in https://github.com/questdb/questdb/pull/6580
feat(pgwire): support VARCHAR[] bind variables for symbol/varchar/str IN expressions by @kafka1991 in https://github.com/questdb/questdb/pull/6574
feat(sql): add column projection pushdown for read_parquet() by @kafka1991 in https://github.com/questdb/questdb/pull/6551
perf(sql): parallel ORDER BY long_column LIMIT N for high-cardinality GROUP BY by @puzpuzpuz in https://github.com/questdb/questdb/pull/6582
fix(sql): fix EXPLAIN UPDATE resulting in error by @bluestreak01 in https://github.com/questdb/questdb/pull/6588
fix(sql): handle quoted column names with dots in JOIN metadata by @jerrinot in https://github.com/questdb/questdb/pull/6590
feat(sql): add rowCount, txn and timestamp columns to tables() by @bluestreak01 in https://github.com/questdb/questdb/pull/6581
perf(sql): speed up min/max aggregates on designated timestamp by @puzpuzpuz in https://github.com/questdb/questdb/pull/6593
fix(sql): inefficient commit batching during parquet exports by @nwoolmer in https://github.com/questdb/questdb/pull/6596
fix(conf): auto-detect native libs in jlink runtime by @jerrinot in https://github.com/questdb/questdb/pull/6493
fix(core): prevent rounding overflow from being ignored during decimal divisions by @RaphDal in https://github.com/questdb/questdb/pull/6598
feat(sql): support PIVOT keyword for rotating rows to columns by @nwoolmer in https://github.com/questdb/questdb/pull/5313
fix(pgwire): allow large varchar column regardless of send buffer size by @jerrinot in https://github.com/questdb/questdb/pull/6603
chore(conf): claude.md to steer agents by @jerrinot in https://github.com/questdb/questdb/pull/6607
feat(core): views by @glasstiger in https://github.com/questdb/questdb/pull/5720
fix(sql): collect dependent columns during top-down projection propagation by @kafka1991 in https://github.com/questdb/questdb/pull/6600
fix(web-console): add polyfill for crypto.randomUUID to be used in insecure contexts by @emrberk in https://github.com/questdb/ui/pull/508
fix(web-console): handle running new queries when there is an operaton in progress by @emrberk in https://github.com/questdb/ui/pull/511
feat(web-console): ai assistant with chat window by @emrberk in https://github.com/questdb/ui/pull/507
feat(web-console): views support by @jerrinot and @emrberk in https://github.com/questdb/ui/pull/512
fix(web-console): fix XSS vulnerability by @bluestreak01 in https://github.com/questdb/ui/pull/519
fix(web-console): declare statements breaking query key parsing by @emrberk in https://github.com/questdb/ui/pull/522
Full Changelog: https://github.com/questdb/questdb/compare/9.2.3...9.3.0