FlatQL
Sign in Start free

FlatQL vs Elasticsearch SQL

FlatQL vs Elasticsearch SQL: what x-pack SQL leaves out

Elasticsearch SQL is the SQL layer built into Elasticsearch. FlatQL is a workbench that can either send statements to it unchanged or run them in its own engine when the cluster says no.

Elasticsearch SQL, often called x-pack SQL after the module it lives in, is Elastic's own answer to "can I just write SQL". The docs describe it as "a feature that allows SQL-like queries to be executed in real-time against Elasticsearch", exposed through a REST API, a translate API, a command-line client, and JDBC and ODBC drivers (overview). It runs inside the cluster and needs nothing installed. FlatQL is a SQL workbench that sits outside the cluster. It speaks to the same HTTP API with your credentials, shows every statement's Query DSL, and adds the pieces of SQL that the built-in layer does not have: joins across indices, CTEs, window functions and warehouse-style functions. The two are not rivals so much as two layers, and FlatQL can drive x-pack SQL directly when that is what you want.

What the docs say, and what FlatQL does with it#

Elasticsearch SQL (x-pack) FlatQL
Form Module inside Elasticsearch; POST /_sql or GET /_sql (API) Web app and desktop app (coming soon); calls the HTTP API, installs nothing
Licence tier SQL APIs and CLI on the free tier; JDBC and ODBC clients from Platinum (subscriptions) Free plan or $199 lifetime Pro; no cluster licence requirement
Interfaces REST, translate API, bin/elasticsearch-sql-cli, JDBC, ODBC (CLI) Browser or desktop editor; Cluster SQL mode forwards to /_sql
Tables per query "Currently FROM supports exactly one table" (SELECT) Any number; joins run in the embedded engine
Joins Not part of the grammar Inner, left, right, full, cross, ASOF; join keys pushed to the other side
Subqueries "supported to a small degree"; none with GROUP BY or HAVING (limitations) Subqueries, CTEs, UNION, HAVING, QUALIFY
Nested fields Cannot be queried directly; one nested field per query (limitations) LATERAL FLATTEN over nested arrays
Arrays "Array fields are not supported" (limitations) Returned as JSON; col:field access
Sorting on aggregates Client-side sort "allows only up to 65535 rows" (limitations) Sort in the engine on any column
Output formats csv, json, tsv, txt, yaml, cbor, smile (formats) Grid, chart, pivot, JSON; export CSV, TSV, JSON, NDJSON, Excel, Parquet
Paging fetch_size default 1000, cursor, clear-cursor API (pagination) Streaming export to disk, up to 5,000,000 rows
Access and history Elasticsearch roles; no query log of its own Per-index roles without cluster users; history by user and connection

Where Elasticsearch SQL is the better fit#

Pick x-pack SQL when the consumer is a program, not a person. A JDBC driver is a JDBC driver: Elastic ships x-pack-sql-jdbc on Maven Central as "a platform independent, stand-alone, Direct to Database, pure Java driver" (JDBC docs), and the ODBC driver plugs Windows BI tools into the cluster once "the server must have Elasticsearch SQL installed and running with the valid license" (ODBC docs). Both need Platinum or above. If you have that licence and a BI tool that expects a driver, use it.

Pick it also when the query fits one index and you want nothing between you and the cluster. A SELECT with a WHERE, a GROUP BY on keyword fields and an ORDER BY on the bucket key translates cleanly, and the translate API shows the Query DSL it became. Scripted pipelines that curl -XPOST /_sql?format=csv work without any account anywhere.

And pick it for the CLI. ./bin/elasticsearch-sql-cli ships with Elasticsearch and runs as "a stand alone Java application" (CLI docs). On a bastion host with no browser, that is the tool.

Where FlatQL is the better fit#

The limitations page is the honest guide here, and FlatQL exists for the lines on it. Three show up constantly.

One table. Every join a warehouse user reaches for is out of scope. FlatQL fetches each side with its own pushed-down filter, sends the join keys of the smaller side as a terms filter to the larger, and joins in DuckDB. The rule set is in the joins guide; the short version is that the engine sees at most about 50,000 rows per index per statement, so filter first.

WITH recent AS (
  SELECT customer_id, amount, placed_at,
         ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY placed_at DESC) AS rn
  FROM orders
  WHERE placed_at >= '2026-09-01' AND status = 'paid'
)
SELECT s.name AS supplier, s.country, COUNT(*) AS customers, SUM(r.amount) AS latest_total
FROM recent AS r
JOIN suppliers AS s ON s.domain = r.customer_id
WHERE r.rn = 1
GROUP BY s.name, s.country
ORDER BY latest_total DESC
LIMIT 20;

x-pack SQL rejects this on the CTE, the window function and the join. In FlatQL the orders filter is pushed as a Query DSL bool filter, the rest runs in the engine, and the result panel labels which was which.

Sub-selects and sorting. "Using sub-selects (SELECT X FROM (SELECT Y)) is supported to a small degree", and sorting an aggregation by anything other than its bucket key falls back to a client-side sort capped at 65,535 rows (limitations). FlatQL's engine has no such split: ORDER BY revenue DESC on a grouped result is an ordinary sort.

Nested and array fields. The docs are direct: nested fields "cannot be used in a query", only one nested field may appear per statement, and "Array fields are not supported". FlatQL flattens orders.items with LATERAL FLATTEN and reads JSON with col:field; see nested fields.

Beyond the grammar, FlatQL adds what a workbench adds: a grid with charts and pivots, exports that stream to disk, Ask FlatQL to draft or explain a statement from the mappings, and a history of every statement with user, connection and duration. Teams get per-index read or write roles enforced by FlatQL on every request, without creating cluster users.

Using both#

Cluster SQL mode is the bridge. Flip it on and FlatQL posts your statement to /_sql exactly as written, then calls /_sql/translate so Show as DSL still displays the Query DSL the cluster built. Use it to confirm that a statement destined for a JDBC pipeline parses the way Elasticsearch expects, or to compare the cluster's plan with FlatQL's. Flip it off and the same worksheet gains joins and window functions. The mode is explained in Cluster SQL mode, and a worked list of what the built-in layer refuses is on the Elasticsearch SQL limitations page.

The same split applies to OpenSearch, whose plugin has a different dialect and a different set of join rules; see FlatQL vs OpenSearch SQL. If your team already lives in Kibana, the Kibana comparison covers how FlatQL fits beside it. Plans, including the Free tier with the full engine, are on the pricing page.

FAQ

Questions

Anything else, write to us.

Does FlatQL replace Elasticsearch SQL or sit on top of it?

Either. In Cluster SQL mode FlatQL posts the statement to the x-pack _sql endpoint and shows the cluster's answer. In Full SQL mode it parses the statement itself, pushes filters and aggregations to the cluster, and runs joins and window functions in its embedded engine. See Cluster SQL mode.

Can Elasticsearch SQL join two indices?

No. The SELECT reference states "Currently FROM supports exactly one table." FlatQL joins indices in its engine, up to about 50,000 rows per index per statement. See joins.

Do I need a Platinum licence to use FlatQL with Elasticsearch?

No. FlatQL talks to the ordinary search and aggregation APIs with your credentials. Elastic's subscriptions page lists the SQL APIs and CLI on the free tier and the JDBC and ODBC clients from Platinum; FlatQL uses neither driver.

Does FlatQL support `_sql` pagination cursors?

In Cluster SQL mode FlatQL handles the fetch and closes the cursor for you. In Full SQL mode results stream from the engine, and exports go to disk up to 5,000,000 rows. See exports.

Start on Free. It stays free.

Connect a cluster and run a join in five minutes. No card, no expiry.

Guides

How FlatQL handles the jobs Elasticsearch makes hard.