SQL extensions

QuestDB attempts to implement standard ANSI SQL. We also try to be compatible with PostgreSQL, although parts of this are a work in progress. This page presents the main extensions we bring to SQL and the main differences that one might find in SQL but not in QuestDB's dialect.

SQL extensions

We have extended SQL to support our data storage model and simplify semantics of time series analytics.

LATEST ON

LATEST ON is a clause introduced to help find the latest entry by timestamp for a given key or combination of keys as part of a SELECT statement.

LATEST ON symbol ID and sideDemo this query
SELECT * FROM trades
WHERE timestamp IN today()
LATEST ON timestamp PARTITION BY symbol, side;

Timestamp search can be performed with regular operators, e.g >, <= etc. However, QuestDB provides a native notation which is faster and less verbose.

Results in a given yearDemo this query
SELECT * FROM trades WHERE timestamp IN '2025';

SAMPLE BY

SAMPLE BY is used for time-based aggregations with an efficient syntax. The short query below will return the average price from a list of symbols by one hour buckets.

SAMPLE BY one month bucketsDemo this query
SELECT timestamp, symbol, sum(price) FROM trades
WHERE timestamp in today()
SAMPLE BY 1h;

Differences from standard SQL

SELECT * FROM is optional

In QuestDB, using SELECT * FROM is optional, so SELECT * FROM my_table; will return the same result as my_table;. While adding SELECT * FROM makes SQL look more complete, there are examples where omitting these keywords makes queries a lot easier to read.

Optional use of SELECT * FROMDemo this query
trades;
-- equivalent to:
SELECT * FROM trades;

GROUP BY is optional

The GROUP BY clause is optional and can be omitted as the QuestDB optimizer derives group-by implementation from the SELECT clause. In standard SQL, users might write a query like the following:

Standard SQL GROUP BYDemo this query
SELECT symbol, side, sum(price) FROM trades
WHERE timestamp IN today()
GROUP BY symbol, side;

However, enumerating a subset of SELECT columns in the GROUP BY clause is redundant and therefore unnecessary. The same SQL in QuestDB SQL-dialect can be written as:

QuestDB Implicit GROUP BYDemo this query
SELECT symbol, side, sum(price) FROM trades
WHERE timestamp IN today();

Implicit HAVING

Let's look at another more complex example using HAVING in standard SQL:

Standard SQL GROUP BY/HAVING
SELECT symbol, side, sum(price) FROM trades
WHERE timestamp IN today()
GROUP BY symbol, side
HAVING sum(price) > 1000;

In QuestDB's dialect, featherweight sub-queries come to the rescue to create a smaller, more readable query, without unnecessary repetitive aggregations. HAVING functionality can be obtained implicitly as follows:

QuestDB Implicit HAVING equivalentDemo this query
(
SELECT symbol, side, sum(price) as total_price
FROM trades WHERE timestamp IN today()
)
WHERE total_price > 10_000_000;