Queries · q112 · Data Protection Officer · week 1, night 1
“How many access/erasure/portability/rectification/restriction requests are we receiving each month, how long do they typically take to close, and how many are overdue or still open?”
Fictional people at a fictional company, each played by a language-model agent. Who they are and what they are told
The manager, at the start of week 1
Subject: A few starting questions on privacy & data handling
Hi Priyanka,
Welcome aboard, and thank you for taking these on this week. As I get oriented, here's what I'd like to understand first. Given the slot constraints, please treat these as priorities in order — if only three fit this week, start from the top and we'll pick up the rest next week.
For now, could you scope the first request for me? I'd like to see it broken down in a way that shows me trend over the last several months, not just a single total — I want to catch whether we're improving or falling behind.
Let me know if any of these need to be split further once you see what the data looks like.
Thanks, Ines
The analyst · week 1, night 1, request 1 of 3
Ines asked me to scope item 1 first (DSAR volume/closure time/overdue, trended monthly) but also flagged items 2 and 3 as high priority and well-defined enough to start in parallel, since the week is tight (5 topics, 4 nights). None of these three depend on each other's results, so I'm running all as independent first-look reports tonight rather than waiting. I kept each to one grain and used a dimension-as-row-grain (purpose, severity) instead of guessing at unknown code values, per the carrier/month pattern in the handbook. Items 4 (retention vs. policy) and 5 (legal holds) are intentionally deferred to a later night since they need review of retention_policies/legal_holds structure first and aren't part of what Ines asked me to scope tonight.
The engineer · 1 dry run on the empty schema, then the submission
WITH t0 AS ( SELECT DISTINCT tcal.month_start AS month_start, tcal.month_label AS month_label FROM calendar_days tcal WHERE tcal.month_start >= DATE '2023-01-01' AND tcal.month_start <= DATE '2025-12-01' ), t1 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS requests_received FROM data_subject_requests tdsr GROUP BY date_trunc('month', tdsr.received_at)::date ), t2 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS access_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'access' GROUP BY date_trunc('month', tdsr.received_at)::date ), t3 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS erasure_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'erasure' GROUP BY date_trunc('month', tdsr.received_at)::date ), t4 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS portability_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'portability' GROUP BY date_trunc('month', tdsr.received_at)::date ), t5 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS rectification_restriction_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type IN ('rectification', 'restriction') GROUP BY date_trunc('month', tdsr.received_at)::date ), t6 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS completed_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NOT NULL GROUP BY date_trunc('month', tdsr.received_at)::date ), t7 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, PERCENTILE_CONT(0.5) WITHIN GROUP ( ORDER BY EXTRACT(EPOCH FROM (tdsr.completed_at - tdsr.received_at)) / 86400.0 ) AS median_days_to_close FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NOT NULL GROUP BY date_trunc('month', tdsr.received_at)::date ), t8 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS still_open_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NULL GROUP BY date_trunc('month', tdsr.received_at)::date ), t9 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS overdue_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NULL AND tdsr.due_at IS NOT NULL AND tdsr.due_at < TIMESTAMP '2026-01-05 00:00:00' GROUP BY date_trunc('month', tdsr.received_at)::date ) SELECT t0.month_label AS month, COALESCE(t1.requests_received, 0) AS requests_received, COALESCE(t2.access_requests_received, 0) AS access_requests_received, COALESCE(t3.erasure_requests_received, 0) AS erasure_requests_received, COALESCE(t4.portability_requests_received, 0) AS portability_requests_received, COALESCE(t5.rectification_restriction_requests_received, 0) AS rectification_restriction_requests_received, COALESCE(t6.completed_count, 0) AS completed_count, ROUND(t7.median_days_to_close, 1) AS median_days_to_close, COALESCE(t8.still_open_count, 0) AS still_open_count, COALESCE(t9.overdue_count, 0) AS overdue_count FROM t0 LEFT JOIN t1 ON t1.month_start = t0.month_start LEFT JOIN t2 ON t2.month_start = t0.month_start LEFT JOIN t3 ON t3.month_start = t0.month_start LEFT JOIN t4 ON t4.month_start = t0.month_start LEFT JOIN t5 ON t5.month_start = t0.month_start LEFT JOIN t6 ON t6.month_start = t0.month_start LEFT JOIN t7 ON t7.month_start = t0.month_start LEFT JOIN t8 ON t8.month_start = t0.month_start LEFT JOIN t9 ON t9.month_start = t0.month_start ORDER BY t0.month_start ASC
“Query compiles with the expected 10 columns at the monthly grain. Submitting now.”
“Submitted. The report builds a full 36-month calendar (2023-01 through 2025-12) via calendar_days and left-joins nine per-month measures from data_subject_requests, each isolated in its own subquery and reconciled with COALESCE so months with zero activity still print as 0 rather than disappearing. Median days-to-close uses PERCENTILE_CONT over fractional days and is left null when no requests closed that month, as requested.”
As Priyanka Desai saw it, from the small dataset the agents worked on
month | requests_received | access_requests_received | erasure_requests_received | portability_requests_received | rectification_restriction_requests_recei | completed_count | median_days_to_close | still_open_count | overdue_count --------+-------------------+--------------------------+---------------------------+-------------------------------+------------------------------------------+-----------------+----------------------+------------------+-------------- 2023-01 | 0 | 0 | 0 | 0 | 0 | 0 | NULL | 0 | 0 2023-02 | 0 | 0 | 0 | 0 | 0 | 0 | NULL | 0 | 0 2023-03 | 0 | 0 | 0 | 0 | 0 | 0 | NULL | 0 | 0 2023-04 | 1 | 0 | 1 | 0 | 0 | 1 | 29.0 | 0 | 0 2023-05 | 3 | 2 | 1 | 0 | 0 | 2 | 22.3 | 1 | 1 2023-06 | 0 | 0 | 0 | 0 | 0 | 0 | NULL | 0 | 0 2023-07 | 0 | 0 | 0 | 0 | 0 | 0 | NULL | 0 | 0 2023-08 | 1 | 0 | 1 | 0 | 0 | 1 | 5.9 | 0 | 0 2023-09 | 2 | 1 | 0 | 0 | 1 | 2 | 27.0 | 0 | 0 2023-10 | 1 | 0 | 1 | 0 | 0 | 1 | 11.3 | 0 | 0 2023-11 | 1 | 0 | 1 | 0 | 0 | 1 | 9.7 | 0 | 0 2023-12 | 1 | 0 | 0 | 0 | 1 | 1 | 7.8 | 0 | 0 2024-01 | 1 | 0 | 1 | 0 | 0 | 1 | 3.5 | 0 | 0 2024-02 | 3 | 0 | 3 | 0 | 0 | 3 | 5.8 | 0 | 0 2024-03 | 2 | 1 | 1 | 0 | 0 | 2 | 12.1 | 0 | 0 2024-04 | 0 | 0 | 0 | 0 | 0 | 0 | NULL | 0 | 0 2024-05 | 3 | 0 | 3 | 0 | 0 | 3 | 9.8 | 0 | 0 2024-06 | 4 | 1 | 2 | 0 | 1 | 4 | 5.0 | 0 | 0 2024-07 | 1 | 0 | 1 | 0 | 0 | 0 | NULL | 1 | 1 2024-08 | 2 | 0 | 2 | 0 | 0 | 2 | 15.6 | 0 | 0 2024-09 | 0 | 0 | 0 | 0 | 0 | 0 | NULL | 0 | 0 2024-10 | 1 | 0 | 1 | 0 | 0 | 1 | 7.5 | 0 | 0 2024-11 | 4 | 0 | 4 | 0 | 0 | 4 | 9.5 | 0 | 0 2024-12 | 2 | 0 | 2 | 0 | 0 | 2 | 10.6 | 0 | 0 2025-01 | 3 | 1 | 2 | 0 | 0 | 3 | 5.4 | 0 | 0 2025-02 | 1 | 1 | 0 | 0 | 0 | 1 | 5.1 | 0 | 0 2025-03 | 2 | 1 | 1 | 0 | 0 | 1 | 15.8 | 1 | 1 2025-04 | 1 | 0 | 1 | 0 | 0 | 1 | 19.8 | 0 | 0 2025-05 | 1 | 0 | 1 | 0 | 0 | 1 | 17.2 | 0 | 0 2025-06 | 1 | 0 | 1 | 0 | 0 | 1 | 14.8 | 0 | 0 2025-07 | 0 | 0 | 0 | 0 | 0 | 0 | NULL | 0 | 0 2025-08 | 1 | 0 | 1 | 0 | 0 | 1 | 3.1 | 0 | 0 2025-09 | 2 | 1 | 1 | 0 | 0 | 2 | 7.9 | 0 | 0 2025-10 | 1 | 0 | 1 | 0 | 0 | 1 | 23.1 | 0 | 0 2025-11 | 0 | 0 | 0 | 0 | 0 | 0 | NULL | 0 | 0 2025-12 | 2 | 1 | 1 | 0 | 0 | 0 | NULL | 2 | 0 (36 rows)
The memo and the queries that followed; links found by title, so inferred
Later queries that build on this report:
Changed only where the benchmark needs the same result on every system
WITH t0 AS ( SELECT DISTINCT tcal.month_start AS month_start, tcal.month_label AS month_label FROM calendar_days tcal WHERE tcal.month_start >= DATE '2023-01-01' AND tcal.month_start <= DATE '2025-12-01'),t1 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS requests_received FROM data_subject_requests tdsr GROUP BY date_trunc('month', tdsr.received_at)::date GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date)),t2 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS access_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'access' GROUP BY date_trunc('month', tdsr.received_at)::date GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date)),t3 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS erasure_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'erasure' GROUP BY date_trunc('month', tdsr.received_at)::date GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date)),t4 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS portability_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'portability' GROUP BY date_trunc('month', tdsr.received_at)::date GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date)),t5 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS rectification_restriction_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type IN ('rectification', 'restriction') GROUP BY date_trunc('month', tdsr.received_at)::date GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date)),t6 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS completed_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NOT NULL GROUP BY date_trunc('month', tdsr.received_at)::date GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date)),t7 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, PERCENTILE_CONT(0.5) WITHIN GROUP ( ORDER BY EXTRACT(EPOCH FROM (tdsr.completed_at - tdsr.received_at)) / 86400.0 ) AS median_days_to_close CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, {{percentile_cont(0.5, {{seconds_between(tdsr.received_at, tdsr.completed_at)}} / 86400.0)}} AS median_days_to_close FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NOT NULL GROUP BY date_trunc('month', tdsr.received_at)::date GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date)),t8 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS still_open_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NULL GROUP BY date_trunc('month', tdsr.received_at)::date GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date)),t9 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS overdue_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NULL AND tdsr.due_at IS NOT NULL AND tdsr.due_at < TIMESTAMP '2026-01-05 00:00:00' GROUP BY date_trunc('month', tdsr.received_at)::date GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date))SELECT t0.month_label AS month, COALESCE(t1.requests_received, 0) AS requests_received, COALESCE(t2.access_requests_received, 0) AS access_requests_received, COALESCE(t3.erasure_requests_received, 0) AS erasure_requests_received, COALESCE(t4.portability_requests_received, 0) AS portability_requests_received, COALESCE(t5.rectification_restriction_requests_received, 0) AS rectification_restriction_requests_received, COALESCE(t6.completed_count, 0) AS completed_count, ROUND(t7.median_days_to_close, 1) AS median_days_to_close, t7.median_days_to_close AS median_days_to_close, COALESCE(t8.still_open_count, 0) AS still_open_count, COALESCE(t9.overdue_count, 0) AS overdue_countFROM t0LEFT JOIN t1 ON t1.month_start = t0.month_startLEFT JOIN t2 ON t2.month_start = t0.month_startLEFT JOIN t3 ON t3.month_start = t0.month_startLEFT JOIN t4 ON t4.month_start = t0.month_startLEFT JOIN t5 ON t5.month_start = t0.month_startLEFT JOIN t6 ON t6.month_start = t0.month_startLEFT JOIN t7 ON t7.month_start = t0.month_startLEFT JOIN t8 ON t8.month_start = t0.month_startLEFT JOIN t9 ON t9.month_start = t0.month_startORDER BY t0.month_start ASCORDER BY t0.month_start ASC NULLS LAST, t0.month_label ASC NULLS LAST
WITH t0 AS ( SELECT DISTINCT tcal.month_start AS month_start, tcal.month_label AS month_label FROM calendar_days tcal WHERE tcal.month_start >= DATE '2023-01-01' AND tcal.month_start <= DATE '2025-12-01' ), t1 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS requests_received FROM data_subject_requests tdsr GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t2 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS access_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'access' GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t3 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS erasure_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'erasure' GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t4 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS portability_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'portability' GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t5 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS rectification_restriction_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type IN ('rectification', 'restriction') GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t6 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS completed_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NOT NULL GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t7 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY CAST(EXTRACT(EPOCH FROM (tdsr.completed_at - tdsr.received_at)) AS NUMERIC(18,6)) / 86400.0) AS median_days_to_close FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NOT NULL GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t8 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS still_open_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NULL GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t9 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS overdue_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NULL AND tdsr.due_at IS NOT NULL AND tdsr.due_at < TIMESTAMP '2026-01-05 00:00:00' GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ) SELECT t0.month_label AS month, COALESCE(t1.requests_received, 0) AS requests_received, COALESCE(t2.access_requests_received, 0) AS access_requests_received, COALESCE(t3.erasure_requests_received, 0) AS erasure_requests_received, COALESCE(t4.portability_requests_received, 0) AS portability_requests_received, COALESCE(t5.rectification_restriction_requests_received, 0) AS rectification_restriction_requests_received, COALESCE(t6.completed_count, 0) AS completed_count, t7.median_days_to_close AS median_days_to_close, COALESCE(t8.still_open_count, 0) AS still_open_count, COALESCE(t9.overdue_count, 0) AS overdue_count FROM t0 LEFT JOIN t1 ON t1.month_start = t0.month_start LEFT JOIN t2 ON t2.month_start = t0.month_start LEFT JOIN t3 ON t3.month_start = t0.month_start LEFT JOIN t4 ON t4.month_start = t0.month_start LEFT JOIN t5 ON t5.month_start = t0.month_start LEFT JOIN t6 ON t6.month_start = t0.month_start LEFT JOIN t7 ON t7.month_start = t0.month_start LEFT JOIN t8 ON t8.month_start = t0.month_start LEFT JOIN t9 ON t9.month_start = t0.month_start ORDER BY t0.month_start ASC NULLS LAST, t0.month_label ASC NULLS LAST
WITH t0 AS ( SELECT DISTINCT tcal.month_start AS month_start, tcal.month_label AS month_label FROM calendar_days tcal WHERE tcal.month_start >= DATE '2023-01-01' AND tcal.month_start <= DATE '2025-12-01' ), t1 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS requests_received FROM data_subject_requests tdsr GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t2 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS access_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'access' GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t3 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS erasure_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'erasure' GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t4 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS portability_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'portability' GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t5 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS rectification_restriction_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type IN ('rectification', 'restriction') GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t6 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS completed_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NOT NULL GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t7 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY CAST(CAST(EXTRACT(EPOCH FROM (tdsr.completed_at - tdsr.received_at)) AS NUMERIC(18,6)) / 86400.0 AS DOUBLE PRECISION)) AS median_days_to_close FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NOT NULL GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t8 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS still_open_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NULL GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t9 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS overdue_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NULL AND tdsr.due_at IS NOT NULL AND tdsr.due_at < TIMESTAMP '2026-01-05 00:00:00' GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ) SELECT t0.month_label AS month, COALESCE(t1.requests_received, 0) AS requests_received, COALESCE(t2.access_requests_received, 0) AS access_requests_received, COALESCE(t3.erasure_requests_received, 0) AS erasure_requests_received, COALESCE(t4.portability_requests_received, 0) AS portability_requests_received, COALESCE(t5.rectification_restriction_requests_received, 0) AS rectification_restriction_requests_received, COALESCE(t6.completed_count, 0) AS completed_count, t7.median_days_to_close AS median_days_to_close, COALESCE(t8.still_open_count, 0) AS still_open_count, COALESCE(t9.overdue_count, 0) AS overdue_count FROM t0 LEFT JOIN t1 ON t1.month_start = t0.month_start LEFT JOIN t2 ON t2.month_start = t0.month_start LEFT JOIN t3 ON t3.month_start = t0.month_start LEFT JOIN t4 ON t4.month_start = t0.month_start LEFT JOIN t5 ON t5.month_start = t0.month_start LEFT JOIN t6 ON t6.month_start = t0.month_start LEFT JOIN t7 ON t7.month_start = t0.month_start LEFT JOIN t8 ON t8.month_start = t0.month_start LEFT JOIN t9 ON t9.month_start = t0.month_start ORDER BY t0.month_start ASC NULLS LAST, t0.month_label ASC NULLS LAST
WITH t0 AS ( SELECT DISTINCT tcal.month_start AS month_start, tcal.month_label AS month_label FROM calendar_days tcal WHERE tcal.month_start >= DATE '2023-01-01' AND tcal.month_start <= DATE '2025-12-01' ), t1 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS requests_received FROM data_subject_requests tdsr GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t2 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS access_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'access' GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t3 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS erasure_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'erasure' GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t4 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS portability_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'portability' GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t5 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS rectification_restriction_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type IN ('rectification', 'restriction') GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t6 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS completed_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NOT NULL GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t7 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, quantileExactInclusive(0.5)(toFloat64(CAST(divideDecimal(toDecimal128(dateDiff('microsecond', tdsr.received_at, tdsr.completed_at), 0), toDecimal128(1000000, 0), 6) AS Nullable(Decimal(18, 6))) / 86400.0)) AS median_days_to_close FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NOT NULL GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t8 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS still_open_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NULL GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ), t9 AS ( SELECT CAST(date_trunc('month', tdsr.received_at) AS date) AS month_start, COUNT(*) AS overdue_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NULL AND tdsr.due_at IS NOT NULL AND tdsr.due_at < TIMESTAMP '2026-01-05 00:00:00' GROUP BY CAST(date_trunc('month', tdsr.received_at) AS date) ) SELECT t0.month_label AS month, COALESCE(t1.requests_received, 0) AS requests_received, COALESCE(t2.access_requests_received, 0) AS access_requests_received, COALESCE(t3.erasure_requests_received, 0) AS erasure_requests_received, COALESCE(t4.portability_requests_received, 0) AS portability_requests_received, COALESCE(t5.rectification_restriction_requests_received, 0) AS rectification_restriction_requests_received, COALESCE(t6.completed_count, 0) AS completed_count, t7.median_days_to_close AS median_days_to_close, COALESCE(t8.still_open_count, 0) AS still_open_count, COALESCE(t9.overdue_count, 0) AS overdue_count FROM t0 LEFT JOIN t1 ON t1.month_start = t0.month_start LEFT JOIN t2 ON t2.month_start = t0.month_start LEFT JOIN t3 ON t3.month_start = t0.month_start LEFT JOIN t4 ON t4.month_start = t0.month_start LEFT JOIN t5 ON t5.month_start = t0.month_start LEFT JOIN t6 ON t6.month_start = t0.month_start LEFT JOIN t7 ON t7.month_start = t0.month_start LEFT JOIN t8 ON t8.month_start = t0.month_start LEFT JOIN t9 ON t9.month_start = t0.month_start ORDER BY t0.month_start ASC NULLS LAST, t0.month_label ASC NULLS LAST
The agents' own records, as the role play wrote them; only the personal data of the person who ran it is redacted
Report request from Priyanka Desai (analyst for the Data Protection Officer):
Title: Data subject requests: monthly volume, closure time, and overdue backlog
Question: How many access/erasure/portability/rectification/restriction requests are we receiving each month, how long do they typically take to close, and how many are overdue or still open?
Grain: one row per calendar month of request receipt (based on data_subject_requests.received_at), from 2023-01 through 2025-12
Columns:
- month (YYYY-MM)
- requests received (total)
- access requests received
- erasure requests received
- portability requests received
- rectification + restriction requests received (combined)
- completed count (received_at in this month AND completed_at is not null, as of 2026-01-05)
- median days to close (completed_at - received_at, in days, for requests completed as of 2026-01-05; null if none completed)
- still open count (received in this month, completed_at is null, as of 2026-01-05)
- overdue count (received in this month, completed_at is null, due_at is not null, due_at < 2026-01-05)
Details: Source: data_subject_requests. Group by date_trunc('month', received_at) or join to calendar_days on received_at date to get month_label. request_type is one of access/erasure/rectification/portability/restriction per the catalog — combine rectification and restriction into one column to stay within the column budget. 'Completed' = completed_at is not null (do not assume a specific status string; confirm status values via dry run if useful, but completed_at is the authoritative closure signal). Median days to close should only use rows with completed_at not null. Overdue is evaluated as of the reporting date 2026-01-05 (do not use CURRENT_DATE; hardcode this literal), restricted to rows where due_at is not null. This is platform-wide (all tenants) since the DPO's question is about Tidewick's overall privacy-request posture, not one merchant. Order by month ascending.
Check your query with the warehouse's dry_run tool, then hand it in with submit.dry_runWITH t0 AS ( SELECT DISTINCT tcal.month_start AS month_start, tcal.month_label AS month_label FROM calendar_days tcal WHERE tcal.month_start >= DATE '2023-01-01' AND tcal.month_start <= DATE '2025-12-01' ), t1 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS requests_received FROM data_subject_requests tdsr GROUP BY date_trunc('month', tdsr.received_at)::date ), t2 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS access_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'access' GROUP BY date_trunc('month', tdsr.received_at)::date ), t3 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS erasure_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'erasure' GROUP BY date_trunc('month', tdsr.received_at)::date ), t4 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS portability_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'portability' GROUP BY date_trunc('month', tdsr.received_at)::date ), t5 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS rectification_restriction_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type IN ('rectification', 'restriction') GROUP BY date_trunc('month', tdsr.received_at)::date ), t6 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS completed_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NOT NULL GROUP BY date_trunc('month', tdsr.received_at)::date ), t7 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, PERCENTILE_CONT(0.5) WITHIN GROUP ( ORDER BY EXTRACT(EPOCH FROM (tdsr.completed_at - tdsr.received_at)) / 86400.0 ) AS median_days_to_close FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NOT NULL GROUP BY date_trunc('month', tdsr.received_at)::date ), t8 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS still_open_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NULL GROUP BY date_trunc('month', tdsr.received_at)::date ), t9 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS overdue_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NULL AND tdsr.due_at IS NOT NULL AND tdsr.due_at < TIMESTAMP '2026-01-05 00:00:00' GROUP BY date_trunc('month', tdsr.received_at)::date ) SELECT t0.month_label AS month, COALESCE(t1.requests_received, 0) AS requests_received, COALESCE(t2.access_requests_received, 0) AS access_requests_received, COALESCE(t3.erasure_requests_received, 0) AS erasure_requests_received, COALESCE(t4.portability_requests_received, 0) AS portability_requests_received, COALESCE(t5.rectification_restriction_requests_received, 0) AS rectification_restriction_requests_received, COALESCE(t6.completed_count, 0) AS completed_count, ROUND(t7.median_days_to_close, 1) AS median_days_to_close, COALESCE(t8.still_open_count, 0) AS still_open_count, COALESCE(t9.overdue_count, 0) AS overdue_count FROM t0 LEFT JOIN t1 ON t1.month_start = t0.month_start LEFT JOIN t2 ON t2.month_start = t0.month_start LEFT JOIN t3 ON t3.month_start = t0.month_start LEFT JOIN t4 ON t4.month_start = t0.month_start LEFT JOIN t5 ON t5.month_start = t0.month_start LEFT JOIN t6 ON t6.month_start = t0.month_start LEFT JOIN t7 ON t7.month_start = t0.month_start LEFT JOIN t8 ON t8.month_start = t0.month_start LEFT JOIN t9 ON t9.month_start = t0.month_start ORDER BY t0.month_start ASC
STATUS: COMPILES (dry run on the empty schema, no data read) COLUMNS: month, requests_received, access_requests_received, erasure_requests_received, portability_requests_received, rectification_restriction_requests_received, completed_count, median_days_to_close, still_open_count, overdue_count
submitWITH t0 AS ( SELECT DISTINCT tcal.month_start AS month_start, tcal.month_label AS month_label FROM calendar_days tcal WHERE tcal.month_start >= DATE '2023-01-01' AND tcal.month_start <= DATE '2025-12-01' ), t1 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS requests_received FROM data_subject_requests tdsr GROUP BY date_trunc('month', tdsr.received_at)::date ), t2 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS access_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'access' GROUP BY date_trunc('month', tdsr.received_at)::date ), t3 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS erasure_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'erasure' GROUP BY date_trunc('month', tdsr.received_at)::date ), t4 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS portability_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type = 'portability' GROUP BY date_trunc('month', tdsr.received_at)::date ), t5 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS rectification_restriction_requests_received FROM data_subject_requests tdsr WHERE tdsr.request_type IN ('rectification', 'restriction') GROUP BY date_trunc('month', tdsr.received_at)::date ), t6 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS completed_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NOT NULL GROUP BY date_trunc('month', tdsr.received_at)::date ), t7 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, PERCENTILE_CONT(0.5) WITHIN GROUP ( ORDER BY EXTRACT(EPOCH FROM (tdsr.completed_at - tdsr.received_at)) / 86400.0 ) AS median_days_to_close FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NOT NULL GROUP BY date_trunc('month', tdsr.received_at)::date ), t8 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS still_open_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NULL GROUP BY date_trunc('month', tdsr.received_at)::date ), t9 AS ( SELECT date_trunc('month', tdsr.received_at)::date AS month_start, COUNT(*) AS overdue_count FROM data_subject_requests tdsr WHERE tdsr.completed_at IS NULL AND tdsr.due_at IS NOT NULL AND tdsr.due_at < TIMESTAMP '2026-01-05 00:00:00' GROUP BY date_trunc('month', tdsr.received_at)::date ) SELECT t0.month_label AS month, COALESCE(t1.requests_received, 0) AS requests_received, COALESCE(t2.access_requests_received, 0) AS access_requests_received, COALESCE(t3.erasure_requests_received, 0) AS erasure_requests_received, COALESCE(t4.portability_requests_received, 0) AS portability_requests_received, COALESCE(t5.rectification_restriction_requests_received, 0) AS rectification_restriction_requests_received, COALESCE(t6.completed_count, 0) AS completed_count, ROUND(t7.median_days_to_close, 1) AS median_days_to_close, COALESCE(t8.still_open_count, 0) AS still_open_count, COALESCE(t9.overdue_count, 0) AS overdue_count FROM t0 LEFT JOIN t1 ON t1.month_start = t0.month_start LEFT JOIN t2 ON t2.month_start = t0.month_start LEFT JOIN t3 ON t3.month_start = t0.month_start LEFT JOIN t4 ON t4.month_start = t0.month_start LEFT JOIN t5 ON t5.month_start = t0.month_start LEFT JOIN t6 ON t6.month_start = t0.month_start LEFT JOIN t7 ON t7.month_start = t0.month_start LEFT JOIN t8 ON t8.month_start = t0.month_start LEFT JOIN t9 ON t9.month_start = t0.month_start ORDER BY t0.month_start ASC
Submitted to tonight's batch. Your work on this request is done.