AQLAnalytics Query Language

The query language built for AI reasoning.

AQL is the high-level query language for analytics. Expressive enough for an AI agent to answer any question, concise enough for a human to trust the answer.

"What's our refund rate?"
metric revenue =
  sum(order_items.quantity * products.price)

metric refunded_revenue =
  revenue | where(orders.status == 'refunded')

metric refund_rate =
  safe_divide(refunded_revenue, revenue)
compiles to your warehouse's native SQL — see how ↓
What it is

A query language that thinks in metrics.

AQL works at the level of metrics, not tables. You define a metric once on your semantic model, then ask for it by name, and AQL compiles that to native SQL.

You write — "revenue by country"
metric revenue =
  sum(order_items.quantity * products.price)

explore {
  dimensions { customers.country }
  measures   { revenue }
}
AQL compiles to
SELECT   c.country,
         SUM(oi.quantity * p.price) AS revenue
FROM     order_items oi
JOIN     products  p ON oi.product_id = p.id
JOIN     orders    o ON oi.order_id   = o.id
JOIN     customers c ON o.customer_id = c.id
GROUP BY c.country
ORDER BY revenue DESC
9–10
AQL, scored by
a BI engineer
"I'd score AQL a 9 or 10. It's up there with the best tools I've used. We can now define metrics based on other metrics, stacking them on top of each other."
Ian Ian BI Engineer, DNSFilter DNSFilter
Under the hood

Built like a language, not glued together from SQL strings.

Most semantic layers store each metric as a SQL string wrapped in YAML, so they inherit SQL's ceiling the moment a metric builds on another metric. AQL makes the metric itself a first-class object you compose like functions, then compiles it to native SQL.

90+ prebuilt functions

The CTE-and-window gymnastics SQL forces on you are single functions here.

percent_of_totalrunning_totalrelative_periodof_allrank+ 80 more
Composable

Break analytics into small steps and chain them with the pipe (|) — modular pieces that snap together like Lego blocks.

Semantic-aware

AQL runs on your semantic model, so queries reference your defined metrics, dimensions, and relationships by name — not raw schema.

Deterministic compilation

The same AQL always compiles to the same SQL — and you can inspect exactly what ran. Nothing hidden.

Database-agnostic

Write once. The compiler speaks the dialect.

PostgresRedshiftBigQuerySnowflakeDatabricksSQL ServerClickHouse
Typed, with IDE feedback

A strong type system validates queries as you write — errors surface in the IDE, not in production dashboards.

Composability

Define once. Compose forever.

Define a metric once against your semantic model. From there, reshape it with the pipe and build new metrics on top. Each one is a reusable object, so logic is never copied or left to drift.

01Define a metric
metric revenue =
  sum(order_items.quantity
      * products.price)

Spans two models, and the join resolves from relationships you defined once. No JOIN written, ever.

02Slice it with the pipe
revenue
  | where(products.category
          == 'Gadgets')

The pipe chains operations left to right. The same metric adapts to any dimension, filter, or time window around it.

03Compose new metrics
metric yoy_change =
  safe_divide(revenue - revenue_ly,
              revenue_ly)

Metrics reference metrics. Change revenue once and every derived metric updates — nothing drifts.

How the AI uses it

The AI does the thinking.
The compiler does the SQL.

Most natural-language tools translate your question straight into SQL. Holistics adds an intermediary step. The AI first writes concise, high-level AQL, which a compiler then turns into your warehouse's SQL.

01Natural languageuser input
"In each region, what's the average revenue of the top 5 countries by users?"
AI · generates
02AQL6 lines · human-readable
metric count_users = count(users.id)
dimensions { region: country.region }
measures {
avg_revenue:
top(5, countries.id, by: count_users) | avg(revenue)
}
Compiler · guarantees
03SQL55 lines · auto-generated
WITH "aql__t3" AS (
SELECT "ecommerce_countries"."continent_name"
AS "ecommerce_countries→continent_name",
"ecommerce_countries"."name" AS "name",
COUNT("ecommerce_users"."id") AS
"count_ecommerce_users→id"
FROM "ecommerce"."users" "ecommerce_users"
JOIN "ecommerce"."countries"
no SQL gymnastics

Routing the AI through AQL instead of straight to SQL pays off on every axis that matters for agentic analytics.

Verifiable in a few readable lines

The answer comes back as a few lines of compact, high-level logic instead of pages of SQL. It's small enough to read, comprehend, and verify yourself.

Reliable, without SQL gymnastics

Working in a high-level language, the AI never sweats database-specific syntax or SQL gymnastics for common analytics, so it's far more accurate than a model writing raw SQL directly.

Governed by your semantic layer

AQL works directly with your semantic layer, so generated logic always uses the correct metric and dimension definitions, and clears access-control checks before it serves an end user.

Capable of the hard questions

Composability and 90+ pre-built analytical functions let it handle far more complex use cases, the long-tail questions that direct SQL generation stumbles on.

The hard questions

The questions that break other tools are one-liners.

Period comparisons, cohorts, percent-of-total, nested aggregation — the analytics that mean a new dataset or a custom SQL block almost everywhere else. With AQL, each one is a single composable expression.

How does revenue compare to last year?
Holistics AI
metric revenue_ly =
  revenue | relative_period(-1 year)

metric yoy_change =
  safe_divide(revenue - revenue_ly, revenue_ly)
✓ reuses endorsed metric · revenue ✓ 4 lines — read it, confirm it, run it
SQLwhat text-to-SQL hands you14 lines to audit
WITH monthly AS (
  SELECT DATE_TRUNC('month', created_at) AS month,
         SUM(amount) AS revenue
  FROM orders
  GROUP BY 1
)
SELECT
  curr.month,
  curr.revenue,
  (curr.revenue - prev.revenue) * 100.0
    / prev.revenue AS yoy_change
FROM monthly curr
LEFT JOIN monthly prev
  ON curr.month = prev.month + INTERVAL '1 year'
What share of revenue does each category contribute?
Holistics AI
metric category_share =
  (revenue * 1.0) / (revenue | of_all(products.category))
✓ reuses endorsed metric · revenue ✓ one function — of_all()
SQLwhat text-to-SQL hands younested windows to audit
SELECT
  p.category,
  SUM(oi.quantity * p.price) * 100.0 /
    SUM(SUM(oi.quantity * p.price)) OVER ()
    AS pct_of_total
FROM order_items oi
JOIN products p ON p.id = oi.product_id
GROUP BY 1
What's our average number of new customers per month?
Holistics AI
users
  | group(users.created_at | month())
  | select(monthly_count: count(users.id))
  | avg(monthly_count)
✓ pipes chain flat — no nesting ✓ 4 lines — read it, confirm it, run it
SQLwhat text-to-SQL hands yousubqueries to audit
SELECT AVG(monthly_count) FROM (
  SELECT
    DATE_TRUNC('month', created_at) AS month,
    COUNT(*) AS monthly_count
  FROM users
  GROUP BY 1
) sub
Which monthly cohorts retain best?
Holistics AI
dimension acquisition_cohort =
  min(orders.created_at | month()) | dimensionalize(users.id)

metric retention =
  (total_users * 1.0) / (total_users | of_all(orders.month_no))
✓ composed from endorsed metrics ✓ 5 lines, not 40 lines of CTEs
SQLwhat text-to-SQL hands you30+ lines to audit
WITH first_orders AS (
  SELECT user_id,
         DATE_TRUNC('month', MIN(created_at))
           AS cohort_month
  FROM orders
  GROUP BY 1
),
activity AS (
  SELECT o.user_id, f.cohort_month, …
… 20+ more lines of CTEs and date math
Give me a 3-month moving average of revenue.
Holistics AI
metric moving_avg_3m =
  trailing_period(revenue, orders.created_at, interval(3 months)) / 3
✓ calendar-aware — gap months handled ✓ 2 lines — read it, confirm it, run it
SQLwhat text-to-SQL hands yousilently wrong on gaps
SELECT month,
  AVG(revenue) OVER (
    ORDER BY month
    ROWS BETWEEN 2 PRECEDING
         AND CURRENT ROW
  ) AS moving_avg_3m
FROM monthly_revenue
-- counts rows, not months: a gap month
-- silently skews the average; fixing it
-- needs a generated calendar spine
Bucket our products into performance quartiles.
Holistics AI
dimension performance_tier =
  case(
    when: ntile(4, order: revenue | desc()) = 1, then: 'Top 25%',
    when: ntile(4, order: revenue | desc()) = 2, then: '25–50%',
    else: 'Bottom 50%'
  ) | dimensionalize(products.id)
✓ a reusable dimension — filter and group by it ✓ 6 lines — read it, confirm it, run it
SQLwhat text-to-SQL hands youone-off, not reusable
SELECT product_id,
  CASE
    WHEN NTILE(4) OVER (ORDER BY revenue DESC) = 1
      THEN 'Top 25%'
    WHEN NTILE(4) OVER (ORDER BY revenue DESC) = 2
      THEN '25–50%'
    ELSE 'Bottom 50%'
  END AS tier
FROM (
  SELECT product_id, SUM(amount) AS revenue
  FROM order_items GROUP BY 1
) t

The harder the question, the more the language matters.

Governed

Every answer traces back to definitions you already governed.

The AI reuses your team's endorsed metrics and composes new ones from existing definitions — it doesn't reinvent "revenue" from raw tables, and access controls in the semantic layer apply automatically.

From catalog · Endorsed
weekly_revenue
sum(orders.amount) by week
Built fromorders.aml · regions.aml
Used by12 dashboards
Not invented from scratch
ai_calc_revenue_v2
sum(orders.total) / 7
No lineage Unverified
Fair questions

"Isn't this just another proprietary language?"

Am I locked in?

AQL compiles to native SQL you can inspect on every query — nothing is hidden. Your definitions are plain-text code in Git, not rows in a vendor database. If you leave, you leave with readable logic.

Why not just SQL?

AQL is a complement, not a replacement. SQL is great at pipelines and ad-hoc exploration; it has no native way to define a metric once and compose it. AQL covers exactly that gap — and when you need raw SQL, passthrough functions (sql_number, agg_text, …) are the escape hatch.

What about the learning curve?

AQL is designed for agents to write and humans to read. The AI writes most of it; your job is to read a few lines and confirm it understood the question — and because the logic is high-level, that read is fast. When you do want to write it yourself, anyone who reads SQL picks it up from the 30-minute guided tour.

"The AI does far more than just fetch answers — it understands the context and generates working solutions, even for edge cases."
Guido StarkGuido StarkHead of Data, World Vision

In production across hundreds of enterprises.