Customer support teams run on data. How many tickets came in today? Which categories are spiking? Which agents are resolving tickets fastest? Which issues stay open for days without anyone touching them?
Most support platforms give you some built-in reporting, but those reports are limited, slow to customize, and completely siloed from the rest of your business data. If your tickets live in a PostgreSQL database whether synced from Zendesk, Intercom, Freshdesk, or built in-house you already have everything you need to build a far more useful analytics dashboard.
This guide walks through building a support ticket analytics dashboard directly from your database. You will write the queries, understand the metrics that matter, and see how tools like AI for Database let you go from raw database access to a live, auto-refreshing dashboard without writing a single line of application code.
The Data Model: What Your Tickets Table Probably Looks Like
Before writing queries, it helps to establish a common baseline. Most support ticket databases whether you built your own or synced data from a third-party tool follow a structure similar to this:
CREATE TABLE tickets (
id SERIAL PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP,
resolved_at TIMESTAMP,
status VARCHAR(20), -- 'open', 'pending', 'resolved', 'closed'
priority VARCHAR(10), -- 'p1', 'p2', 'p3', 'low', 'normal', 'high'
category VARCHAR(50),
assigned_to VARCHAR(100), -- agent name or ID
team VARCHAR(50),
customer_id INT,
subject TEXT,
channel VARCHAR(20) -- 'email', 'chat', 'phone', 'web'
);Your schema will differ, but the concepts translate directly. The queries below use this structure. Adjust column names as needed.
Metric 1: Ticket Volume by Category
The most fundamental view is how tickets are distributed across categories. This tells you where customer pain concentrates.
SELECT
category,
COUNT(*) AS ticket_count,
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 1) AS pct_of_total
FROM tickets
WHERE created_at >= NOW() - INTERVAL '30 days'
GROUP BY category
ORDER BY ticket_count DESC;This gives you both the raw count and each category's share of total volume. If "billing" jumps from 12% to 28% in a week, something changed a pricing update, an invoice bug, a billing cycle edge case. The dashboard surfaces that signal immediately.
Run this query weekly and you will start to see seasonal patterns, product release impacts, and the downstream effect of marketing campaigns.
Metric 2: Average Resolution Time by Team
Knowing that tickets exist is one thing. Knowing how fast they get resolved and which teams are slowest is where analytics becomes operational.
SELECT
team,
COUNT(*) AS resolved_tickets,
ROUND(AVG(
EXTRACT(EPOCH FROM (resolved_at - created_at)) / 3600
), 1) AS avg_hours_to_resolve,
ROUND(PERCENTILE_CONT(0.5) WITHIN GROUP (
ORDER BY EXTRACT(EPOCH FROM (resolved_at - created_at)) / 3600
), 1) AS median_hours_to_resolve
FROM tickets
WHERE
resolved_at IS NOT NULL
AND created_at >= NOW() - INTERVAL '30 days'
GROUP BY team
ORDER BY avg_hours_to_resolve ASC;This query returns both mean and median resolution time. Median matters because a handful of very old tickets can inflate the average dramatically. If your median is 4 hours but your mean is 22 hours, you have a small number of tickets sitting unresolved for days which is a separate problem worth surfacing.
Metric 3: Open vs. Closed Tickets Over Time
A snapshot of your backlog at any given moment is useful. A trend over time tells you whether things are getting better or worse.
SELECT
DATE_TRUNC('day', created_at) AS day,
COUNT(*) FILTER (WHERE status IN ('open', 'pending')) AS opened,
COUNT(*) FILTER (WHERE status IN ('resolved', 'closed')) AS closed
FROM tickets
WHERE created_at >= NOW() - INTERVAL '14 days'
GROUP BY DATE_TRUNC('day', created_at)
ORDER BY day ASC;Charting this as a dual-line graph gives you an immediate visual of whether your team is keeping up with inbound volume. When the "opened" line consistently runs above "closed," your queue is growing. If you see that trend for three or four days in a row, that is your signal to staff up, triage more aggressively, or investigate why volume spiked.
Metric 4: Priority Distribution and SLA Compliance
Not all tickets are equal. P1 issues production outages, billing failures, data loss have much tighter SLA requirements than general inquiries. Your dashboard needs a clear view of how tickets break down by priority and whether high-priority issues are being handled within target windows.
SELECT
priority,
COUNT(*) AS total,
COUNT(*) FILTER (WHERE status IN ('open', 'pending')) AS still_open,
COUNT(*) FILTER (
WHERE resolved_at IS NOT NULL
AND EXTRACT(EPOCH FROM (resolved_at - created_at)) / 3600 <= CASE
WHEN priority = 'p1' THEN 4
WHEN priority = 'p2' THEN 24
ELSE 72
END
) AS resolved_within_sla
FROM tickets
WHERE created_at >= NOW() - INTERVAL '30 days'
GROUP BY priority
ORDER BY priority;The CASE statement encodes your SLA targets directly into the query. P1 tickets should resolve within 4 hours, P2 within 24 hours, everything else within 72 hours. Adjust those numbers to match your actual SLA commitments.
This query is particularly useful for support leadership. If P1 SLA compliance drops below 90%, that conversation needs to happen today, not at the end of the month.
Metric 5: Agent Performance
Understanding which agents handle the most tickets and resolve them fastest helps with workforce planning, coaching, and identifying your top performers.
SELECT
assigned_to AS agent,
COUNT(*) AS tickets_handled,
COUNT(*) FILTER (WHERE status IN ('resolved', 'closed')) AS tickets_resolved,
ROUND(AVG(
EXTRACT(EPOCH FROM (resolved_at - created_at)) / 3600
) FILTER (WHERE resolved_at IS NOT NULL), 1) AS avg_resolve_hours,
COUNT(*) FILTER (WHERE status IN ('open', 'pending')) AS currently_open
FROM tickets
WHERE created_at >= NOW() - INTERVAL '30 days'
GROUP BY assigned_to
ORDER BY tickets_resolved DESC;Read this table carefully. An agent with high volume and low resolve time is performing well. An agent with high volume and high resolve time may be overloaded or working on harder tickets. An agent with low volume and high open tickets may have a backlog building up quietly.
From Queries to a Live Dashboard
Writing these queries is the first step. The harder part is turning them into something useful a dashboard that refreshes automatically, is shareable with non-technical stakeholders, and does not require you to re-run queries manually every time someone wants an update.
This is exactly where AI for Database fits in.
Connect your database (PostgreSQL, MySQL, Supabase, BigQuery, and more are supported), and you can start asking questions in plain English:
The tool translates those questions to SQL, runs them against your live database, and returns a table or chart. No query editor required for your ops manager or support lead.
More importantly, you can save those queries as dashboard panels. The dashboard refreshes on a schedule hourly, daily, weekly so stakeholders always see current data without anyone having to manually run anything.
The workflow feature takes this a step further. You can define conditions like: "when the count of open P1 tickets exceeds 10, send an alert to the support Slack channel." That runs automatically against your database on whatever interval you set. Your support manager gets notified before a P1 backlog becomes a crisis.
Start Tracking What Actually Matters
Support ticket analytics is not a reporting problem it is a visibility problem. The data is already in your database. The challenge is surfacing it quickly enough to be useful, and keeping it current without manual effort.
The queries in this guide give you a solid analytical foundation: volume by category, resolution time by team, open vs. closed trends, priority distribution, SLA compliance, and agent performance. Build from these and add metrics that matter to your specific support operation.
If you want to skip the dashboard infrastructure work and get straight to the insights, try AI for Database free at aifordatabase.com.