Hybrid queries
Hybrid queries let you embed FeatureQL inside SQL — or SQL inside FeatureQL. This means you can use FeatureQL features from any SQL client, BI tool, or application without changing your existing workflows.
Language directives
When FeatureMesh receives a query, it needs to know which language to parse. Use comment directives to be explicit:
/* FEATUREQL */ -- Parse as FeatureQL
/* SQL */ -- Parse as SQL Without a directive, FeatureMesh auto-detects the language. Explicit directives are safer, especially in hybrid queries where both languages appear.
Two embedding syntaxes
FeatureQL blocks can be embedded in SQL using two syntaxes:
| Syntax | Example | When to use |
|---|---|---|
| Parentheses (default) | FEATUREQL(SELECT ...) | Standard syntax — use this by default |
| Comment | FEATUREQL/* SELECT ... */ | For SQL clients that parse or introspect queries and choke on the parentheses form |
The parentheses syntax is cleaner and supports nesting. The comment syntax exists because some external tools (BI platforms, SQL linters, query formatters) inspect query text and may reject FEATUREQL(...) as invalid SQL. Wrapping the FeatureQL body in a comment (/* ... */) makes it invisible to those tools.
Both syntaxes produce identical results. Pick the one that works with your toolchain.
FeatureQL as a subtable
Wrap a FeatureQL query in FEATUREQL(...) to use it as a table expression inside SQL. The outer SQL can filter, join, or aggregate the results:
/* SQL */
SELECT
STORE_IS_BCN,
COUNT(*) AS NUM
FROM FEATUREQL(
WITH
STORES := ENTITY(),
STORE_ID := INPUT(BIGINT#STORES),
DIM_STORE_ATTRIBUTES := EXTERNAL_SQL(
`SELECT store_id, store_city FROM (VALUES (1, 'BCN'), (2, 'BCN'), (3, 'MAD'), (4, 'MAD'), (5, 'VAL')) as t(store_id, store_city) WHERE store_id=%STORE_ID`
AS ROW(store_id BIGINT#STORES, store_city VARCHAR)
),
STORE_CITY := DIM_STORE_ATTRIBUTES[store_city],
SELECT
STORE_ID := BIND_VALUES(ARRAY[1,2,3,4,5]),
STORE_CITY,
STORE_CITY='BCN' AS STORE_IS_BCN
)
GROUP BY STORE_IS_BCN| STORE_IS_BCN BOOLEAN | NUM VARCHAR |
|---|---|
| false | 3 |
| true | 2 |
The FeatureQL block computes features for each store, and the outer SQL groups the results by STORE_IS_BCN — a pattern that's natural when your BI tool expects SQL but your metrics live in FeatureQL.
FeatureQL as a CTE
FeatureQL blocks work as Common Table Expressions too. This separates feature computation from downstream analysis:
/* SQL */
WITH
a AS FEATUREQL(
WITH
FEATURE1 := INPUT(BIGINT)
SELECT
FEATURE1 := BIND_VALUES(SEQUENCE(1,10)),
FEATURE2 := FEATURE1 * 2
)
SELECT * FROM a WHERE FEATURE2 < 6| FEATURE1 BIGINT | FEATURE2 VARCHAR |
|---|---|
| 1 | 2 |
| 2 | 4 |
The CTE a computes features via FeatureQL, and the outer query filters with standard SQL. You can reference the same CTE multiple times in the main query, and the engine caches the results.
The same query using the comment syntax:
/* SQL */
WITH
a AS FEATUREQL/*
WITH
FEATURE1 := INPUT(BIGINT)
SELECT
FEATURE1 := BIND_VALUES(SEQUENCE(1,10)),
FEATURE2 := FEATURE1 * 2
*/
SELECT * FROM a WHERE FEATURE2 < 6| FEATURE1 BIGINT | FEATURE2 VARCHAR |
|---|---|
| 1 | 2 |
| 2 | 4 |
Working with SQL clients
Many SQL clients (Tableau, DBeaver, Superset) automatically wrap user queries:
SELECT * FROM (/* User's query */) LIMIT 1000 Hybrid syntax fits naturally into this pattern. The example below uses the comment syntax (FEATUREQL/* ... */), which is better suited here — SQL clients that introspect the outer query won't be confused by the embedded FeatureQL:
SELECT *, FEATURE1 + FEATURE2 AS SQL_COLUMN
FROM (
/* SQL */
FEATUREQL /*
WITH
FEATURE1 := INPUT(BIGINT)
SELECT
FEATURE1 := BIND_VALUES(SEQUENCE(1,10)),
FEATURE2 := FEATURE1 * 2
*/
)
ORDER BY FEATURE1
LIMIT 3| FEATURE1 BIGINT | FEATURE2 BIGINT | SQL_COLUMN VARCHAR |
|---|---|---|
| 1 | 2 | 3 |
| 2 | 4 | 6 |
| 3 | 6 | 9 |
The SQL_COLUMN in the outer query demonstrates that you can reference FeatureQL output columns in SQL expressions — the boundary between the two languages is transparent.
Compatible clients: Tableau, PowerBI, Looker, Superset, DataGrip, DBeaver, and any application using JDBC/ODBC.
Limitations
- FeatureQL does not optimize across language boundaries — each FeatureQL block is compiled independently, and filter pushdown depends on the underlying engine.
- Hybrid queries are not supported on the DataFusion backend.
- When possible, prefer pure FeatureQL for better maintainability and optimization. Use hybrid queries when you need to integrate with existing SQL workflows or tools that require SQL input.