Below are copy/paste-ready SQLs (MySQL/MariaDB style) you can use to power executive panels from your trino_queries table.

Assumptions: • You have a timestamp column somewhere (you didn’t show one). Exec dashboards need time. If you don’t have it, add it (recommended):

ALTER TABLE trino_queries ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, ADD COLUMN finished_at TIMESTAMP NULL, ADD INDEX idx_created_at (created_at), ADD INDEX idx_state_created (query_state, created_at), ADD INDEX idx_user_created (user, created_at), ADD INDEX idx_catalog_schema_created (catalog, schema, created_at);

If you already have created_at/finished_at, just use yours.

1) Executive KPI Cards (last 24h / 7d)

1A. Core KPI cards (Queries, Success rate, Active users, P95 latency)

SELECT COUNT() AS total_queries, SUM(query_state = ‘FINISHED’) AS finished_queries, ROUND(100 * SUM(query_state = ‘FINISHED’) / NULLIF(COUNT(), 0), 2) AS success_rate_pct, COUNT(DISTINCT user) AS active_users, ROUND(AVG(wall_time_millis) / 1000, 2) AS avg_wall_seconds, ROUND( (SELECT wall_time_millis FROM trino_queries q2 WHERE q2.created_at >= NOW() - INTERVAL 7 DAY ORDER BY wall_time_millis LIMIT 1 OFFSET FLOOR(0.95 * (SELECT COUNT(*) FROM trino_queries q3 WHERE q3.created_at >= NOW() - INTERVAL 7 DAY)) ) / 1000, 2 ) AS p95_wall_seconds FROM trino_queries WHERE created_at >= NOW() - INTERVAL 7 DAY;

Note: MySQL doesn’t have a native percentile_cont in many versions; the OFFSET trick works but can be heavy. For dashboards, consider pre-aggregating (see section 8).

1B. Week-over-week growth (queries + active users)

SELECT this_week.total_queries AS this_week_queries, last_week.total_queries AS last_week_queries, ROUND(100 * (this_week.total_queries - last_week.total_queries) / NULLIF(last_week.total_queries, 0), 2) AS wow_queries_pct, this_week.active_users AS this_week_users, last_week.active_users AS last_week_users, ROUND(100 * (this_week.active_users - last_week.active_users) / NULLIF(last_week.active_users, 0), 2) AS wow_users_pct FROM (SELECT COUNT() total_queries, COUNT(DISTINCT user) active_users FROM trino_queries WHERE created_at >= CURDATE() - INTERVAL 7 DAY) this_week CROSS JOIN (SELECT COUNT() total_queries, COUNT(DISTINCT user) active_users FROM trino_queries WHERE created_at >= CURDATE() - INTERVAL 14 DAY AND created_at < CURDATE() - INTERVAL 7 DAY) last_week;

2) Adoption & Usage Trends

2A. Daily queries + active users trend (time series)

SELECT DATE(created_at) AS day, COUNT(*) AS queries, COUNT(DISTINCT user) AS users FROM trino_queries WHERE created_at >= CURDATE() - INTERVAL 30 DAY GROUP BY DATE(created_at) ORDER BY day;

2B. Top teams/domains by source (or resource group)

SELECT COALESCE(source, ‘unknown’) AS source, COUNT(*) AS queries, COUNT(DISTINCT user) AS users FROM trino_queries WHERE created_at >= NOW() - INTERVAL 30 DAY GROUP BY COALESCE(source, ‘unknown’) ORDER BY queries DESC LIMIT 20;

2C. Top catalogs/schemas driving platform usage

SELECT COALESCE(catalog, ‘unknown’) AS catalog, COALESCE(schema, ‘unknown’) AS schema_name, COUNT(*) AS queries, COUNT(DISTINCT user) AS users FROM trino_queries WHERE created_at >= NOW() - INTERVAL 30 DAY GROUP BY COALESCE(catalog, ‘unknown’), COALESCE(schema, ‘unknown’) ORDER BY queries DESC LIMIT 30;

3) Reliability & SLA

3A. Success / failure rates by day

SELECT DATE(created_at) AS day, COUNT() AS total, SUM(query_state = ‘FINISHED’) AS finished, SUM(query_state IN (‘FAILED’)) AS failed, SUM(query_state IN (‘CANCELED’)) AS canceled, ROUND(100 * SUM(query_state = ‘FINISHED’) / NULLIF(COUNT(),0), 2) AS success_pct FROM trino_queries WHERE created_at >= CURDATE() - INTERVAL 30 DAY GROUP BY DATE(created_at) ORDER BY day;

3B. Failure reasons (error_type / error_code / failure_type)

SELECT COALESCE(error_type, ‘unknown’) AS error_type, COALESCE(failure_type, ‘unknown’) AS failure_type, COALESCE(error_code, ‘unknown’) AS error_code, COUNT(*) AS failures FROM trino_queries WHERE created_at >= NOW() - INTERVAL 30 DAY AND query_state = ‘FAILED’ GROUP BY COALESCE(error_type, ‘unknown’), COALESCE(failure_type, ‘unknown’), COALESCE(error_code, ‘unknown’) ORDER BY failures DESC LIMIT 30;

3C. Queueing pain (p95 queued time over time)

SELECT DATE(created_at) AS day, ROUND(AVG(queued_time_millis) / 1000, 2) AS avg_queue_s, ROUND(MAX(queued_time_millis) / 1000, 2) AS max_queue_s FROM trino_queries WHERE created_at >= CURDATE() - INTERVAL 30 DAY GROUP BY DATE(created_at) ORDER BY day;

4) Cost & Efficiency (best exec panel set)

4A. Total “compute seconds” trend (proxy cost = cpu_time_millis)

SELECT DATE(created_at) AS day, ROUND(SUM(cpu_time_millis) / 1000, 2) AS cpu_seconds, ROUND(SUM(wall_time_millis) / 1000, 2) AS wall_seconds, COUNT(*) AS queries FROM trino_queries WHERE created_at >= CURDATE() - INTERVAL 30 DAY GROUP BY DATE(created_at) ORDER BY day;

4B. Cost by team/source (top 20)

SELECT COALESCE(source, ‘unknown’) AS source, ROUND(SUM(cpu_time_millis) / 1000, 2) AS cpu_seconds, COUNT() AS queries, ROUND((SUM(cpu_time_millis) / 1000) / NULLIF(COUNT(),0), 2) AS cpu_seconds_per_query FROM trino_queries WHERE created_at >= NOW() - INTERVAL 30 DAY GROUP BY COALESCE(source, ‘unknown’) ORDER BY cpu_seconds DESC LIMIT 20;

4C. “Waste” panel: failed + canceled CPU burn

SELECT DATE(created_at) AS day, ROUND(SUM(CASE WHEN query_state IN (‘FAILED’,’CANCELED’) THEN cpu_time_millis ELSE 0 END) / 1000, 2) AS wasted_cpu_seconds, ROUND(100 * SUM(CASE WHEN query_state IN (‘FAILED’,’CANCELED’) THEN cpu_time_millis ELSE 0 END) / NULLIF(SUM(cpu_time_millis),0), 2) AS wasted_cpu_pct FROM trino_queries WHERE created_at >= CURDATE() - INTERVAL 30 DAY GROUP BY DATE(created_at) ORDER BY day;

4D. Top 20 most expensive queries (for drill-down)

SELECT query_id, user, COALESCE(source,’unknown’) AS source, COALESCE(catalog,’unknown’) AS catalog, COALESCE(schema,’unknown’) AS schema_name, ROUND(cpu_time_millis/1000, 2) AS cpu_seconds, ROUND(wall_time_millis/1000, 2) AS wall_seconds, peak_memory_bytes, LEFT(query, 300) AS query_preview FROM trino_queries WHERE created_at >= NOW() - INTERVAL 7 DAY ORDER BY cpu_time_millis DESC LIMIT 20;

5) Performance (P50/P95 style panels)

5A. Latency buckets (simple exec-friendly histogram)

SELECT CASE WHEN wall_time_millis < 1000 THEN ‘<1s’ WHEN wall_time_millis < 5000 THEN ‘1-5s’ WHEN wall_time_millis < 30000 THEN ‘5-30s’ WHEN wall_time_millis < 120000 THEN ‘30-120s’ ELSE ‘>120s’ END AS latency_bucket, COUNT(*) AS queries FROM trino_queries WHERE created_at >= NOW() - INTERVAL 7 DAY GROUP BY latency_bucket ORDER BY queries DESC;

5B. Slow queries trend (% over 30s)

SELECT DATE(created_at) AS day, ROUND(100 * SUM(wall_time_millis >= 30000) / NULLIF(COUNT(*),0), 2) AS slow_query_pct FROM trino_queries WHERE created_at >= CURDATE() - INTERVAL 30 DAY GROUP BY DATE(created_at) ORDER BY day;

6) Data Movement & Volume (what execs call “scale”)

6A. Daily bytes scanned / output

SELECT DATE(created_at) AS day, SUM(physical_input_bytes) AS physical_input_bytes, SUM(output_bytes) AS output_bytes, SUM(written_bytes) AS written_bytes, SUM(total_rows) AS total_rows FROM trino_queries WHERE created_at >= CURDATE() - INTERVAL 30 DAY GROUP BY DATE(created_at) ORDER BY day;

6B. “Big scans” top sources (drives infra bills)

SELECT COALESCE(source,’unknown’) AS source, SUM(physical_input_bytes) AS scanned_bytes, COUNT() AS queries, ROUND(SUM(physical_input_bytes) / NULLIF(COUNT(),0)) AS avg_scan_bytes FROM trino_queries WHERE created_at >= NOW() - INTERVAL 30 DAY GROUP BY COALESCE(source,’unknown’) ORDER BY scanned_bytes DESC LIMIT 20;

7) Governance / Security Proxies (given your schema)

You don’t have explicit “policy decision” columns here, but you can still create exec panels using proxies:

7A. Queries touching “sensitive” catalogs/schemas (define your list)

SELECT DATE(created_at) AS day, COUNT(*) AS sensitive_queries FROM trino_queries WHERE created_at >= CURDATE() - INTERVAL 30 DAY AND ( catalog IN (‘pii_catalog’, ‘security_catalog’) OR schema IN (‘pii’, ‘hr’, ‘customer_sensitive’) ) GROUP BY DATE(created_at) ORDER BY day;

7B. Top users accessing sensitive areas

SELECT user, COUNT(*) AS sensitive_queries, ROUND(SUM(cpu_time_millis)/1000,2) AS cpu_seconds FROM trino_queries WHERE created_at >= NOW() - INTERVAL 30 DAY AND (catalog IN (‘pii_catalog’,’security_catalog’) OR schema IN (‘pii’,’hr’,’customer_sensitive’)) GROUP BY user ORDER BY sensitive_queries DESC LIMIT 20;

If you later integrate OPA/Moat decisions, add columns like policy_decision, policy_id, masked_columns_json and these panels become first-class.

8) Make Dashboards Fast: Pre-Aggregated Exec Tables

For Grafana/exec dashboards, you’ll want summary tables refreshed every 1–5 mins (or hourly).

8A. Daily exec rollup table

CREATE TABLE IF NOT EXISTS trino_exec_daily ( day DATE NOT NULL PRIMARY KEY, total_queries BIGINT NOT NULL, finished BIGINT NOT NULL, failed BIGINT NOT NULL, canceled BIGINT NOT NULL, active_users BIGINT NOT NULL, cpu_seconds DOUBLE NOT NULL, wall_seconds DOUBLE NOT NULL, avg_wall_s DOUBLE NOT NULL, slow_pct DOUBLE NOT NULL, wasted_cpu_pct DOUBLE NOT NULL );

8B. Populate/refresh (idempotent upsert)

REPLACE INTO trino_exec_daily SELECT DATE(created_at) AS day, COUNT() AS total_queries, SUM(query_state = ‘FINISHED’) AS finished, SUM(query_state = ‘FAILED’) AS failed, SUM(query_state = ‘CANCELED’) AS canceled, COUNT(DISTINCT user) AS active_users, SUM(cpu_time_millis) / 1000 AS cpu_seconds, SUM(wall_time_millis) / 1000 AS wall_seconds, AVG(wall_time_millis) / 1000 AS avg_wall_s, 100 * SUM(wall_time_millis >= 30000) / NULLIF(COUNT(),0) AS slow_pct, 100 * SUM(CASE WHEN query_state IN (‘FAILED’,’CANCELED’) THEN cpu_time_millis ELSE 0 END) / NULLIF(SUM(cpu_time_millis),0) AS wasted_cpu_pct FROM trino_queries WHERE created_at >= CURDATE() - INTERVAL 90 DAY GROUP BY DATE(created_at);

9) Grafana Panel Mapping (quick)

Use these as panels: • KPI cards: Section 1A • Usage trend: 2A • Top teams: 2B • Success rate trend: 3A • Cost trend: 4A • Cost by team: 4B • Waste trend: 4C • Latency buckets: 5A • Big scan drivers: 6B • Sensitive access trend: 7A

If you tell me what timestamp column you actually have (or if you don’t), and your Trino states (FINISHED/FAILED/RUNNING etc), I can tailor these queries exactly to your dataset and also give you the materialized rollup tables for hourly + 5-min refresh (Grafana-friendly).