---
title: "ChatGPT Prompt to Write SQL Queries"
description: "A ChatGPT prompt to write SQL queries for product managers: the table facts to include, the three concepts to check, and how to debug a number that is too big."
canonical_url: "https://builderscamp.com/guides/tools/sql-prompt-template-for-product-managers"
date_published: "2026-09-27"
date_modified: "2026-09-27"
author: "Andre Albuquerque"
publisher: "Builders Camp"
guide_class: "tools"
---

# A ChatGPT prompt to write SQL queries that get the numbers right

**TL;DR:** A ChatGPT prompt to write SQL queries works when it carries what the model cannot see: the dialect, the business question, what one row of each table means, how the tables join, the exact filters, and the output you want. Then check three things before trusting the number: table granularity, which COUNT you used, and whether a join multiplied your rows.

AI can write the SQL. It cannot see your database. The creators of the [Spider 2.0 benchmark](https://spider2-sql.github.io/), which tests models on real enterprise data workflows, report that "For widely-used models like GPT-4o, the success rate is only 10.1% on Spider 2.0 tasks, compared to 86.6% on Spider 1.0", the older benchmark built on simpler, cleaner schemas. A benchmark is not your warehouse, and model scores change with every release, but the gap between the two benchmarks shows where the risk sits. The syntax is rarely what goes wrong. The context is.

So the prompt has two jobs: give the model the facts about your tables that it would otherwise guess, and make it show its assumptions so you can catch the wrong ones. The three concepts further down are the minimum you need to check the result.

The running example is original: a B2B SaaS product with three tables, `workspaces` (one row per customer workspace), `members` (one row per person per workspace) and `invoices` (one row per invoice sent to a workspace).

## What should a ChatGPT prompt to write SQL queries include?

Seven parts, each answering something the model cannot infer from a question alone.

```
You are a careful analyst writing [PostgreSQL / BigQuery / Snowflake] SQL.

Business question: [the question in plain words, and the decision it feeds]

Tables:
- workspaces: one row per workspace. Columns: workspace_id, plan
  ('free', 'team', 'business'), created_at (UTC), churned_at (null if active)
- members: one row per person per workspace. Columns: member_id,
  workspace_id, role, joined_at
- invoices: one row per invoice. Columns: invoice_id, workspace_id,
  amount_eur, issued_at, status ('paid', 'open', 'void')

Joins: members.workspace_id and invoices.workspace_id both point to
workspaces.workspace_id.

Filters: [exact plans, statuses, and date window, with time zone]

Output: [columns, one row per what, sort order]

Before writing the query, list every assumption you are making about
the data. After the query, explain in one sentence per clause what it
does, and say which row counts I should check.
```

Two lines do most of the work. "One row per" for each table tells the model the granularity, which decides whether a join is safe. "List every assumption" turns silent guesses into a list you can correct, such as the model assuming `void` invoices count as revenue. For the general version of this structure across PM work, see the [prompt template for product managers](https://builderscamp.com/guides/templates/prompt-template-for-product-managers).

## What is table granularity, and why check it first?

Table granularity is what one row represents. In the example, one row of `workspaces` is a workspace, one row of `members` is a person in a workspace, and one row of `invoices` is an invoice. A question about workspaces answered from the `members` table counts people, not workspaces, and the query will run without complaint.

Three ways to find it: read the table's documentation if it exists, look at a few rows and see what repeats, or find the column that is unique on every row (the primary key). If `workspace_id` repeats in a table, that table is not one row per workspace, whatever its name suggests.

## Which COUNT should the query use?

The three forms of COUNT answer different questions, and the model will pick one unless you say which. The [PostgreSQL documentation](https://www.postgresql.org/docs/current/functions-aggregate.html) defines `count(*)` as the function that "Computes the number of input rows", while `count(column)` "Computes the number of input rows in which the input value is not null."

| Form | Counts | Example on `members` |
|---|---|---|
| `COUNT(*)` | Every row | Member rows, including one person in two workspaces twice |
| `COUNT(role)` | Rows where `role` is not null | Members with a role set |
| `COUNT(DISTINCT workspace_id)` | Unique values | Workspaces that have at least one member |

When the question says "how many customers", the answer is almost always a `COUNT(DISTINCT ...)` on the customer identifier. When the model writes `COUNT(*)`, ask it which entity each row represents at that point in the query.

## When does an inner join lose rows, and when does a left join keep them?

An inner join keeps only rows that match on both sides. A left join keeps every row from the first table and fills the other side with nulls where there is no match.

In the example, "average revenue per workspace last quarter" with an inner join from `workspaces` to `invoices` silently drops every free workspace, because free workspaces have no invoices. The average goes up, and it now describes paying workspaces only. A left join keeps them, with null revenue you can turn into zero. Neither join is wrong. The question decides which one is right, and the prompt should say which population the answer is about.

## How do you debug a number that looks too high?

A number that looks too high is usually a join that multiplied rows. Here is an original walkthrough.

You ask: "Total paid invoice revenue last quarter for Team plan workspaces, and how many members they have." The model joins `workspaces` to `invoices` and to `members` in one query, then sums `amount_eur`. The result is €1.26 million. Finance reports about €180,000 for the same plans and quarter.

Work through it in order:

1. **Check the filters.** Confirm the plan value is exactly `'team'`, the status is `'paid'`, and the quarter boundaries use the same time zone as finance. Ask for the distinct values of each filter column if unsure.
2. **Count before the join.** Run the invoice part alone: say 900 paid invoices summing to €180,000. That matches finance.
3. **Count after the join.** Run the joined query with `COUNT(*)` and `COUNT(DISTINCT invoice_id)`. If `COUNT(*)` is 6,300 and the distinct count is 900, each invoice now appears seven times on average.
4. **Find the multiplying table.** `members` is one row per person per workspace. Joining it attaches every invoice to every member of that workspace, so a workspace with seven members has its revenue counted seven times.
5. **Fix the query shape.** Sum invoices and count members separately, each grouped by workspace, then join the two results. Or ask two questions in two queries.

Tell the model what you found, state the granularity of each table again, and ask it to rewrite the query. The step that catches the bug is step 3, and it takes one extra line of SQL. For pulling the same kind of answer without writing SQL at all, [pulling product data with Claude Code](https://builderscamp.com/guides/tools/claude-code-data-pull-without-sql) covers the agent route and the same join check.

## When is AI-written SQL not good enough on its own?

The strongest objection to PMs writing queries with AI is that a confident wrong number is worse than no number: it gets pasted into a deck and defended. The objection is fair, and the answer is not to stop, but to know where your checking ends. Stop and ask an analyst when you cannot explain every clause in the query, when the query needs window functions or nested subqueries you cannot read, when the number will drive a large or public decision, or when checking it has taken longer than the question was worth.

Below that line, the prompt and the three checks above catch most errors. Above it, the query is a draft for someone who knows the data to review. The [ChatGPT for product managers](https://builderscamp.com/guides/tools/chatgpt-for-product-managers) guide covers other everyday uses, and a [metrics tree](https://builderscamp.com/guides/glossary/metrics-tree) helps decide which numbers are worth querying in the first place.

Builders Camp runs Data for Product Managers as a 2 week bootcamp with 4 live sessions and 8 self-paced microlessons, directed by Andre Albuquerque, in the Product Management Starter Track, the Discovery Expert Track and the Data & Analytics Specialist Track. Its public topics cover metrics that matter, funnels and activation, retention and cohorts, segmentation, experimentation and A/B testing basics, and communicating insights, and its practical challenge includes writing SQL to diagnose a subscription product.

[See the Data for Product Managers bootcamp](https://builderscamp.com/bootcamps/data-for-pm?utm_source=guide&utm_medium=organic&utm_campaign=sql-prompt-template-for-product-managers)

Save the Tables block of the prompt somewhere you can reuse it, such as a ChatGPT project or a custom instruction. Writing "one row per" for each table once means every later question starts with the granularity already stated.

## Frequently asked questions

### What is a good ChatGPT prompt to write SQL queries?

One that states the SQL dialect, the business question in plain words, what one row of each table represents, how the tables join, the exact filters and time window, and the output you want. End it by asking the model to list its assumptions before the query. The same prompt works in ChatGPT, Claude or Gemini.

### How much SQL does a product manager need to know to use AI for queries?

Enough to read the query, not to write it from scratch. The three concepts worth knowing well are table granularity (what one row means), the difference between COUNT(*), COUNT(column) and COUNT(DISTINCT column), and the difference between an inner and a left join. Those three explain most wrong numbers.

### Can ChatGPT write accurate SQL?

For simple questions on a small, well-described schema, usually yes. On realistic enterprise databases it is much weaker: the Spider 2.0 benchmark site reports GPT-4o solving 10.1 percent of its tasks, against 86.6 percent on the older, simpler Spider 1.0. The difference is mostly context, which is why the prompt has to carry the table facts the model cannot see.

### Why is my SQL number too high?

Almost always a join that multiplies rows. If you join a table with one row per customer to a table with one row per event, each customer appears once per event, and any SUM or COUNT(*) afterwards is inflated. Compare COUNT(*) with COUNT(DISTINCT customer_id) before and after the join to find it.

### Why does my query return nothing?

Usually a filter that matches no values: a status stored as 'Active' when you wrote 'active', a date column in UTC when you filtered on local time, or an inner join to a table with no matching rows. Ask the model to list likely causes, then run a query for the distinct values of each filter column.

### Should I paste real data into ChatGPT?

Paste the schema and a few made-up example rows, not customer data. Column names, types and what each row represents are enough for the model to write the query. Check your company's policy on which AI tools are approved for internal data before pasting anything real.

### When should I stop and ask an analyst?

When the query needs features you cannot read, such as window functions or nested subqueries, when the number will drive a large or public decision, or when you have spent longer checking the result than the question was worth. A second pair of eyes on the join is cheaper than a wrong number in a board deck.

## Sources

- [Spider 2.0: Evaluating Language Models on Real-World Enterprise Text-to-SQL Workflows](https://spider2-sql.github.io/)
- [PostgreSQL documentation: Aggregate Functions](https://www.postgresql.org/docs/current/functions-aggregate.html)

## How this guide was made

Researched from Builders Camp's bootcamp, track and masterclass material and the sources listed on this page, drafted with AI, and fact-checked against every source cited.
