TECH · JUNE 2026

The Agent Doesn't Have a SQL Problem

Data EngineeringAI & Tools

For twenty years, getting an answer out of your data meant going through a chart someone built for you in advance. You wanted a number, you found the dashboard that had it, and if the dashboard didn't have it you filed a ticket and waited a week for an analyst to get to you. That era is ending. The next thing in business reporting is conversation, where you ask your data a question in plain English and it answers, with no dashboard and no ticket and no analyst sitting in the middle. Every serious data platform is moving this way, Snowflake included, and once you've used it properly you don't want to go back.

The catch is that doing this well and doing it at all are very different problems. Pointing an agent at your warehouse so it can answer questions is close to a weekend project. Getting it to answer correctly, every time, in a way people will actually trust, is the whole job, and that job has a name most teams skip straight past. It's the semantic layer.

I've spent the last few months building one over our data in Snowflake, and most of what follows is what that work taught me. I'm writing it down to make the case for doing it properly and to save the next person some of the bruises, because the lessons were more expensive to learn than I'd like to admit and there's no reason they should cost you the same.

Here's what going without one looks like. Picture a Monday standup: someone wired an AI agent to the warehouse over the weekend, and it works. You ask it for revenue last quarter and it answers in four seconds. The room is impressed. So three people go back to their desks and ask it the same thing in their own words, and they get three different numbers. Close enough to look right, far enough apart to start an argument. By Wednesday nobody trusts the agent, and by Friday it's quietly unplugged.

"What was our revenue last quarter?"
$1.24MFinance
$1.41MSales ops
$0.98MMarketing

Same question, same agent, three desks. Close enough to look right, far enough apart to start an argument.

I've watched a version of this play out more than once, and it always traces back to the same mistake. We assume the hard part of analytics is writing SQL, so when a model writes SQL on its own, we think we're done. The hard part was never the SQL. The hard part is knowing what the question means, and that is exactly the part we handed to a system that has no way of knowing.

Schema is not meaning

Hand an agent your raw warehouse and here is what it actually sees. A table called dim_cust_v2. A column called rev_amt_net_usd. Another one called rev_gross, and a third nobody has touched since 2023 called revenue_final_DO_NOT_USE. The agent reads these names the way you'd read street signs in a language you don't speak. It can pronounce them. It has no idea where they go.

What the agent sees

Street signs in a language it doesn't speak. It can read the names. It can't tell you where they go.

So it guesses. That is the whole problem in one word. When you ask for "active customers," it has to decide whether active means logged in this month, or has a paid subscription, or simply was never marked deleted, and it will pick one without telling you which. When you ask for "revenue," it picks a column. When the answer depends on which date you count by, order date or ship date or invoice date, it picks one of those too. Every one of these is a real business decision your finance team settled years ago after a long meeting, and the agent re-litigates all of them silently, on every single query, and gets a coin flip's worth of them wrong.

The failures you don't see

The dangerous failures here are not the loud ones. A query that errors out is fine, you see it and move on. The query that returns a clean, plausible, wrong number is the one that costs you.

Joins are where this turns ugly. An agent that naively joins two fact tables, orders and shipments, fans out the rows and double counts the revenue. The total still looks like a total. It's just inflated by however many shipments each order had, and unless you already know the right answer you will never catch it by looking. Multiply that across a quarter of decisions made on inflated numbers and you understand why "the agent said so" is not a sentence anyone should be comfortable saying out loud.

1 order$100
→ JOIN shipments →
shipment 1 · $100
shipment 2 · $100
shipment 3 · $100
SUM(order_total) = $300double-counted, should be $100

The total still looks like a total. It's just inflated by however many shipments each order had.

Then there's consistency. The old BI world papered over the meaning problem by hardcoding it into dashboards, one definition of revenue baked into one chart, never to be questioned. Agents tear that paper off, because they generate queries fresh every time instead of reading from a frozen report. That makes them more flexible and far more dangerous, because now the same question asked twice can produce two answers, and trust in a metrics system dies the first time that happens in front of a VP.

And before any of that, there's the simple matter of context. People imagine the fix is to dump the whole schema into the prompt and let the model sort it out. A real warehouse has thousands of columns. Stuffing all of them into context doesn't just cost tokens, it lowers accuracy, because you've buried the ten columns that matter under four thousand that don't. More schema makes the agent worse, not better.

What a Snowflake semantic view actually is

This is the gap a semantic layer fills, and on Snowflake it now has a name and a shape. A semantic view is a real database object. You CREATE SEMANTIC VIEW and inside it you declare the things the warehouse never bothered to write down.

You map your logical tables and describe them in plain language. You declare the relationships between them, with the join keys and the cardinality, so the model knows that orders to shipments is one to many and stops double counting on its own. You define your dimensions and your facts, and on top of the facts you define metrics, the actual aggregations like SUM(order_total), named once and computed the same way forever. Crucially, every one of these carries a description, a set of synonyms, and sample values, so when a user says "sales" or "bookings" or "GMV" the model knows they all point at the same metric, and when it sees a status column it already knows the legal values are active, churned, and trial.

To make that concrete, say you have two tables you'd want an agent to reason over: a customers table and an orders table. Stripped down, a semantic view across them looks like this.

CREATE OR REPLACE SEMANTIC VIEW customer_analytics

  TABLES (
    customers AS analytics.public.dim_customers
      PRIMARY KEY (customer_id)
      WITH SYNONYMS ('clients', 'accounts')
      COMMENT = 'One row per customer',
    orders AS analytics.public.fct_orders
      PRIMARY KEY (order_id)
      COMMENT = 'One row per placed order'
  )

  -- how the tables actually relate: many orders to one customer
  RELATIONSHIPS (
    orders_to_customers AS
      orders (customer_id) REFERENCES customers (customer_id)
  )

  FACTS (
    orders.order_total AS order_total
      COMMENT = 'Net order value in USD, excludes tax and refunds'
  )

  DIMENSIONS (
    customers.status AS status
      WITH SYNONYMS ('account status')
      COMMENT = 'Lifecycle state: active, churned, trial',
    orders.order_date AS order_date
      COMMENT = 'Date the order was placed, not shipped'
  )

  METRICS (
    orders.total_revenue AS SUM(orders.order_total)
      WITH SYNONYMS ('revenue', 'sales', 'GMV')
      COMMENT = 'The one and only definition of revenue',
    customers.customer_count AS COUNT(DISTINCT customers.customer_id)
      COMMENT = 'Distinct customer count, sliced by any dimension'
  )

  COMMENT = 'Customers and orders model for the analytics agent';

Look at what's written down there that a raw schema never says out loud. Revenue is SUM(order_total) and nothing else, defined once. Orders relate to customers as many-to-one, so a customer count can't silently fan out. The date that counts is order date, not ship date. And when someone types "clients" or "sales" or "GMV," the model already knows exactly which object they mean. None of that lives in the column names. All of it lives here, on purpose.

The piece that does the most work, and the one most people skip, is the verified query repository. These are known-good question-and-SQL pairs that a human has signed off on. Cortex Analyst, the service that reads the semantic view and turns English into SQL, uses them as worked examples. It isn't improvising from scratch against a wall of column names anymore, it's pattern matching against questions your team has already answered correctly. That single difference is most of the accuracy gap. Snowflake reports Cortex Analyst landing well into the high range on a well-built semantic model, and open text-to-SQL benchmarks run straight at raw schemas have spent years stuck well below that.

Put end to end, a single question travels like this.

1 · User Asks in plain English: "What was our revenue last quarter?"
2 · Agent Hands the question to Cortex Analyst instead of guessing at SQL itself
3 · Semantic view Resolves "revenue" to the total_revenue metric, finds the order→customer join, and pulls the closest verified query
4 · Generated SQL Right metric, right join, no fan-out, instead of a guess against raw column names
5 · Snowflake runs it Row-level security and masking on the underlying tables still apply
6 · Answer One number the user can trust, the same every time it's asked

The semantic view sits in the middle, turning a plain-English question into SQL that means what the business means.

Designing one for an agent, not a dashboard

If you've built semantic layers for BI before, the instinct carries over but the priorities shift, and this is where the real work lives.

The first shift is that descriptions now matter more than names. A human analyst could puzzle out what rev_amt_net_usd means from context and tribal knowledge. The agent only has the description you wrote, so a lazy one-line description is a bug, not a cosmetic miss. The second shift is to curate ruthlessly. The temptation is to expose everything for completeness, but a smaller model of forty well-described, genuinely-used entities beats a complete model of four hundred, every time, both for accuracy and for cost. Leave the junk drawer out.

Beyond that, declare your grain and relationships explicitly rather than hoping the model infers them, because it won't. Invest in verified queries the way you'd invest in tests, seeding the repository with the questions you already know people ask and growing it from real usage. Design so that genuine ambiguity makes the agent ask a clarifying question instead of guessing, and Cortex Analyst will do this if the model gives it the room. Let governance flow through untouched, because row-level security and masking policies on the underlying tables still apply, which means the agent inherits each user's permissions for free instead of becoming a brand new way to leak data. And build an eval set, a fixed bank of questions with known-correct answers, so that when you change the semantic view you can measure whether you made it smarter or just different.

The work moves, it doesn't disappear

The thing I keep coming back to is that the semantic layer doesn't remove the hard human decisions. It just moves them somewhere honest. Defining what revenue means, which date counts, how the tables really relate, those were always the job. We used to bury them inside dashboards and the heads of three senior analysts. Now we write them down in one place the agent can read, on purpose, where they can be reviewed and corrected and trusted.

The agent was never going to understand your business. It was only ever going to navigate the model of your business you took the trouble to build. Skip that part and you don't have an analytics agent, you have a very fast, very confident way to be wrong.

Coming next

A semantic view tells the agent what your data means, but meaning is only the first layer. Point an agent at a real request, written the way people actually write them, and harder problems surface in the data itself. A brief asking for a "luxury" audience pulls back luxury cars when you meant fashion, because the agent is matching words against labels instead of intent. Ask for one brand and you can end up with its competitors, because to a vector two rival names look almost identical. A phrase like "high-income parents in the Northeast, excluding existing customers" has to become precise set logic before any of it can run.

None of that lives in the semantic view. It lives in the data you feed the agent, and getting it ready is its own story. I'm writing that one next: everything beyond the semantic view that has to happen to your data before an agent can be trusted to act on it.