The Agent Doesn't Have a SQL Problem
Data EngineeringAI & ToolsFor 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.
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.
dim_cust_v2customers? and which version is the live one?rev_amt_net_usdnet of what: tax, refunds, discounts?rev_grossor is revenue this one instead?revenue_final_DO_NOT_USE...so which one is final?
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.
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.
total_revenue metric, finds the order→customer join, and pulls the closest verified query
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.