SQL Query Generator for Product Managers

Ask about activation, churn, feature adoption, or NPS — in plain English. Chion generates verified SQL and a chart. No SQL knowledge required; the query is visible if your engineering team wants to review it.

Why PMs use Chion

Ask product questions in plain English — no SQL required.

You don't need to write SQL. Type your question — "how many users activated last week?" — and Chion generates verified SQL, runs it read-only, and renders a chart. The SQL is displayed under every chart so your data team can verify the logic.

Follow-up questions maintain context. Ask "break that down by plan tier" and Chion builds on the previous query without starting over.

Examples: PM questions → SQL

Real product questions, verified SQL output.

"How many users activated last week?"

COUNT DISTINCT + date filter
SELECT COUNT(DISTINCT user_id) AS activated_users
FROM user_events
WHERE event_name = 'activation'
  AND event_date >= CURRENT_DATE - INTERVAL '7 days';

"Feature adoption rate by plan tier"

LEFT JOIN + CASE + ratio
SELECT p.plan_name,
  COUNT(DISTINCT CASE WHEN e.event_name = 'feature_used' THEN e.user_id END)::numeric
  / NULLIF(COUNT(DISTINCT u.id), 0) * 100 AS adoption_rate_pct
FROM users u
JOIN plans p ON p.id = u.plan_id
LEFT JOIN user_events e ON e.user_id = u.id
GROUP BY p.plan_name
ORDER BY adoption_rate_pct DESC;

"Churn rate month over month"

Multi-CTE + ratio
WITH monthly AS (
  SELECT DATE_TRUNC('month', canceled_at) AS month, COUNT(*) AS churned
  FROM subscriptions WHERE canceled_at IS NOT NULL GROUP BY 1
),
active AS (
  SELECT DATE_TRUNC('month', period_start) AS month, COUNT(DISTINCT user_id) AS active
  FROM subscriptions GROUP BY 1
)
SELECT m.month, m.churned, a.active,
  ROUND(m.churned::numeric / NULLIF(a.active, 0) * 100, 1) AS churn_rate_pct
FROM monthly m JOIN active a ON a.month = m.month
ORDER BY m.month;

"NPS breakdown by customer segment"

FILTER + NPS formula
SELECT segment,
  COUNT(*) FILTER (WHERE score >= 9) AS promoters,
  COUNT(*) FILTER (WHERE score BETWEEN 7 AND 8) AS passives,
  COUNT(*) FILTER (WHERE score <= 6) AS detractors,
  ROUND(
    (COUNT(*) FILTER (WHERE score >= 9) - COUNT(*) FILTER (WHERE score <= 6))::numeric
    / NULLIF(COUNT(*), 0) * 100, 1
  ) AS nps
FROM nps_responses
GROUP BY segment;

PM FAQ

Try it — no SQL required

Connect your PostgreSQL database and ask about activation, churn, or feature adoption.