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
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:

SyntaxExampleWhen to use
Parentheses (default)FEATUREQL(SELECT ...)Standard syntax — use this by default
CommentFEATUREQL/* 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:

FeatureQL
/* 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
Result
STORE_IS_BCN BOOLEANNUM VARCHAR
false3
true2

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:

FeatureQL
/* 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
Result
FEATURE1 BIGINTFEATURE2 VARCHAR
12
24

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:

FeatureQL
/* 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
Result
FEATURE1 BIGINTFEATURE2 VARCHAR
12
24

Working with SQL clients

Many SQL clients (Tableau, DBeaver, Superset) automatically wrap user queries:

SELECT * FROM (/* User's query */) LIMIT 1000
sql

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:

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
Result
FEATURE1 BIGINTFEATURE2 BIGINTSQL_COLUMN VARCHAR
123
246
369

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.
Last update at: 2026/03/03 16:47:38
Last updated: 2026-03-03 16:48:19