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.

1Define oncein your semantic model
metric revenue =
  sum(order_items.quantity * products.price)
2Ask by name"revenue by country"
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 metrics as SQL strings, so composition breaks the moment one metric builds on another. AQL makes each metric a real object you compose like functions, then compiles 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

Define a metric once, and others build on it by reference. Metrics compose like functions, instead of repeating each other's SQL.

Semantic-aware

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

No black box

AQL is a compile step, not a runtime. It outputs standard SQL that runs natively in your warehouse, with no proprietary engine in between.

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.

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
The percentage stuff is really powerful — percentage of the whole, of this dimension, of that one. It's powerful because percentage is always "but of what?"
Alex Hancock · Director, Data / Digital / Loyalty, Pinter
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.

On cohort analysis
"AQL lets me create a cohort view I couldn't do before. Every time I wanted a different type of cohort, I had to go back to the model and add to it — now I can just do it. A dynamic, flexible cohort viz, for a retention person, is really powerful for understanding the drivers behind the successful cohorts."
Alex Hancock Director, Data / Digital / Loyalty, Pinter
Composability

Define once. Compose forever.

A metric in AQL is a real semantic object, not a SQL string — so the AI builds on it like code, instead of pasting SQL together.

01
Metrics are first-class objects
metric revenue =
  sum(order_items.quantity
      * products.price)

Semantic-aware — it knows your models and relationships, so the join resolves itself. There's no SQL text to copy.

so
02
Real composition, not string concatenation
metric refunded_revenue =
  revenue | where(orders.status == 'refunded')

metric refund_rate =
  safe_divide(refunded_revenue, revenue)

Each metric builds on the one before by reference, like functions. A string-based tool would re-embed all of revenue's SQL here — AQL never does.

Define it once and it stays one definition — change revenue and everything built on it follows, and the same metric works in any report, sliced any way.

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
Lineage
orders.aml regions.aml
weekly_revenue
12 dashboards revenue report
Catalog status
weekly_revenue Endorsed
regional_revenue Endorsed
ai_calc_revenue_v2 Blocked

The AI composes from endorsed definitions — it never invents an unverified metric to answer a question.

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.