DB Olympics

Queries · q327 · Data Protection Officer · week 2, night 2

Data export fulfillment audit: are access/portability exports actually completing?

“Is 'zero completed data exports' real, or is 'completed' simply the wrong status string for this table — and are shoppers who filed access requests actually receiving their data?”

Manager · asked the questionInes LaurentData Protection Officer. Lawyer by training, previously at a European marketplace. Also reports to the board's audit committee.
Analyst · planned the reportPriyanka DesaiAuditor's mindset: traces individual records end to end to check that the totals are real.
Engineer · wrote the SQLViktorOld-school warehouse engineer: explicit JOIN chains straight to the base tables, derived tables, GROUP BY with CASE pivots, few CTEs.

Fictional people at a fictional company, each played by a language-model agent. Who they are and what they are told

  1. A business event lands on the Data Protection Officer's desk

    Week 2; shared with Henrik Aalto, Chief Information Security Officer

    A data protection authority asked how we handle privacy: how many privacy requests we receive and how fast we complete them, how consent is recorded, and what remains after an account is deleted.

  2. Ines Laurent writes to Priyanka Desai

    The manager, at the start of week 2

    Subject: Week 2 — DPA inquiry changes our priorities

    Priyanka,

    Good work — this is exactly the kind of digging I wanted. A few things surprised me and now matter more because of the regulator inquiry (which I'm handling jointly with Henrik Aalto, CISO — our response needs to be accurate and defensible, not just reassuring).

    What struck me:

    • The three "open" erasure requests aren't really open, they're parked behind legal holds — but we don't actually know if those holds are still justified. If a regulator asked us today why those erasures haven't happened, "disputes" that are 1.5–2.6 years old is not a comfortable answer without evidence.
    • We have a closure-tracking gap (rejected requests never get a completion date) and apparently zero portability requests in three years. Both could be true, or both could be an artifact of how we log things — and I don't want to hand the regulator a number I can't stand behind.
    • The email retention failure is concrete and fixable, but it also means I can't yet answer "what remains after an account is deleted" with confidence for other categories until we know how widespread this is.
    • The consent anomaly (one category showing suspiciously perfect opt-in) undermines my confidence in saying "this is how consent is recorded" to a regulator until it's explained.

    So, for this week, please prioritize work that lets us answer the DPA's three questions honestly: volume and speed of privacy requests, how consent is captured and whether it's reliable, and what data persists post-deletion. That's the deliverable Henrik and I need to brief the board audit committee before we respond.

    My questions:

    1. For the three long-running legal holds, can we establish — from whatever evidence exists — whether the underlying disputes are still active, so I can decide whether to push for release or document why they must stand?
    2. Can we get a trustworthy, defensible count of privacy requests received and resolved, including the rejected ones and whether "zero portability requests ever" is real or a logging blind spot — the regulator will ask for exactly this?
    3. How confident can we be in what consent records actually show, given the anomaly you flagged — do we have other categories with the same too-clean pattern, and does that change how I'd describe our consent process to a regulator?
    4. Beyond email, how much shopper data elsewhere is sitting past its stated retention window right now — I need a true current picture of "what remains after deletion," not just the one confirmed violation.

    If all four can't fit, start with 2 and 4 — those go directly into the regulator response.

    Thanks, Ines

    A note from the Chief Information Security Officer, Henrik Aalto

    Subject: Security angle for the DPA response

    Hi Priyanka,

    A few things from my side that would strengthen the privacy response:

    1. When a data subject request results in erasure, can we show what login/session and access history remains afterward, and for how long — i.e. do our retention rules actually clear it out, or does it linger?
    2. For consent withdrawals, do we have any evidence that we stop using/collecting the related data promptly (e.g. no further sign-ins or sensitive-data access tied to that account after withdrawal)?
    3. On the completion-time question the DPA is asking about — can we also show whether requests touching account security data (sessions, API/service credentials, access logs) take longer to close than others? That's often where delays hide.

    Happy to compare notes before you send anything to data engineering.

    Henrik

  3. Priyanka Desai plans the night

    The analyst · week 2, night 2, request 2 of 3

    Two things from last night need resolving before I trust the numbers for the regulator: (1) report 1 showed DSAR received_at dates as early as 2020-04-24 and 2022-08-25 — before the stated 2023-01-01 warehouse history start — which could be legitimate backfilled compliance records carried over from the pre-migration system, or could be a data quality artifact; I need to see these specific rows and compare received_at to created_at to tell which. (2) report 2 showed all 11 data_exports linked to a DSAR, but completed_export_count was 0, and the engineer flagged uncertainty about whether 'completed' is even the right status string for that table — if genuinely zero exports ever finished, that's a real fulfillment failure (shoppers who filed access requests may never have actually received their data), separate from the "portability doesn't exist as a request_type" question I already closed. Both are quick, cheap diagnostics. With priorities 2 and 4 now largely resolved (volumes reconcile to 60 pending the pre-2023 check; retention is fine everywhere except email_log, confirmed last night), I'm using my third slot to start priority 1 for Ines: looking for whatever internal evidence exists (payment status, support ticket activity) on the 3 held accounts, since there's no explicit "dispute" table and the legal holds aren't linked to a specific order/payment — I have to reconstruct it from the subject_user_id.

    Data export fulfillment audit: are access/portability exports actually completing?
    Question
    Is 'zero completed data exports' real, or is 'completed' simply the wrong status string for this table — and are shoppers who filed access requests actually receiving their data?
    One row per
    data_exports.status value
    Columns
    • status
    • count
    • earliest requested_at
    • latest requested_at
    • count with completed_at not null
    • count with file_url not null (artifact actually produced)
    • average row_count (for rows where row_count is not null)
  4. Viktor writes the SQL

    The engineer · 1 dry run on the empty schema, then the submission

    Dry run 1 · STATUS: COMPILES (dry run on the empty schema, no data read)
    SELECT
      status,
      COUNT(*) AS export_count,
      MIN(requested_at) AS earliest_requested_at,
      MAX(requested_at) AS latest_requested_at,
      COUNT(completed_at) AS count_with_completed_at,
      COUNT(file_url) AS count_with_file_url,
      ROUND(AVG(row_count) FILTER (WHERE row_count IS NOT NULL), 2) AS avg_row_count
    FROM data_exports
    GROUP BY status
    ORDER BY export_count DESC, status ASC

    “Query compiles cleanly.”

    Submitted · the SQL of the last dry run, unchanged · Submitted to tonight's batch. Your work on this request is done.

    “Submitted. The report groups data_exports by its literal status value (no assumption about which string means "done"), with counts, date range, and how often completed_at/file_url are actually populated per status — which will directly show whether "completed" is just a naming mismatch or a real fulfillment gap.”

  5. The report comes back overnight

    As Priyanka Desai saw it, from the small dataset the agents worked on

    status  | export_count | earliest_requested_at      | latest_requested_at        | count_with_completed_at | count_with_file_url | avg_row_count
    --------+--------------+----------------------------+----------------------------+-------------------------+---------------------+--------------
    expired |           11 | 2022-08-25 23:06:26.754811 | 2025-09-01 06:56:20.570772 |                      11 |                  11 |       1456.09
    (1 rows)
  6. What came of it

    The memo and the queries that followed; links found by title, so inferred

    It builds on:

  7. It becomes benchmark query q327

    Changed only where the benchmark needs the same result on every system

    • numericsdocs/NUMERICS.md: rounding removed; results keep full precision; AVG over an integer computed as a decimal: AVG(CAST(x AS NUMERIC(18,6)))
    • determinismthe standard leaves the place of NULLs in a sort to the implementation: NULLS LAST ascending, NULLS FIRST descending, as in the reference results (2×)
    The benchmark text, against the submitted SQL
    SELECT  status,  COUNT(*) AS export_count,  MIN(requested_at) AS earliest_requested_at,  MAX(requested_at) AS latest_requested_at,  COUNT(completed_at) AS count_with_completed_at,  COUNT(file_url) AS count_with_file_url,  ROUND(AVG(row_count) FILTER (WHERE row_count IS NOT NULL), 2) AS avg_row_count  AVG(CAST(row_count AS NUMERIC(18,6))) FILTER (WHERE row_count IS NOT NULL) AS avg_row_countFROM data_exportsGROUP BY statusORDER BY export_count DESC, status ASCORDER BY export_count DESC NULLS FIRST, status ASC NULLS LAST
    SELECT
      status,
      COUNT(*) AS export_count,
      MIN(requested_at) AS earliest_requested_at,
      MAX(requested_at) AS latest_requested_at,
      COUNT(completed_at) AS count_with_completed_at,
      COUNT(file_url) AS count_with_file_url,
      AVG(CAST(row_count AS NUMERIC(18,6))) FILTER (WHERE row_count IS NOT NULL) AS avg_row_count
    FROM data_exports
    GROUP BY status
    ORDER BY export_count DESC NULLS FIRST, status ASC NULLS LAST
    1 ms10 ms100 msHyper2.9 msDuckDB4.3 msGizmoSQL6.5 msclickhouse_brew6.6 msFirebolt9.9 msDataFusion28.8 msClickHouse4.5 ms
  8. Everything, as recorded

    The agents' own records, as the role play wrote them; only the personal data of the person who ran it is redacted

    Viktor's whole session, 7 turns
    • The request, as Viktor received it
      Report request from Priyanka Desai (analyst for the Data Protection Officer):
      
      Title: Data export fulfillment audit: are access/portability exports actually completing?
      Question: Is 'zero completed data exports' real, or is 'completed' simply the wrong status string for this table — and are shoppers who filed access requests actually receiving their data?
      Grain: one row per data_exports.status value
      Columns:
      - status
      - count
      - earliest requested_at
      - latest requested_at
      - count with completed_at not null
      - count with file_url not null (artifact actually produced)
      - average row_count (for rows where row_count is not null)
      Details: Source: data_exports, platform-wide, full history (all 11 rows from last night's linked set, in case there are more unlinked ones this surfaces as well). Group by status exactly as stored. This resolves whether last night's completed_export_count = 0 was a wrong assumption about the status string, or whether it's genuinely true that no export has ever been marked complete — in which case we need to check whether file_url/completed_at tell a different, more reassuring story. Order by count descending.
      
      Check your query with the warehouse's dry_run tool, then hand it in with submit.
    • Viktor calls dry_run
      SELECT
        status,
        COUNT(*) AS export_count,
        MIN(requested_at) AS earliest_requested_at,
        MAX(requested_at) AS latest_requested_at,
        COUNT(completed_at) AS count_with_completed_at,
        COUNT(file_url) AS count_with_file_url,
        ROUND(AVG(row_count) FILTER (WHERE row_count IS NOT NULL), 2) AS avg_row_count
      FROM data_exports
      GROUP BY status
      ORDER BY export_count DESC, status ASC
    • The warehouse answers
      STATUS: COMPILES (dry run on the empty schema, no data read)
      COLUMNS: status, export_count, earliest_requested_at, latest_requested_at, count_with_completed_at, count_with_file_url, avg_row_count
    • ViktorQuery compiles cleanly.
    • Viktor calls submit
      SELECT
        status,
        COUNT(*) AS export_count,
        MIN(requested_at) AS earliest_requested_at,
        MAX(requested_at) AS latest_requested_at,
        COUNT(completed_at) AS count_with_completed_at,
        COUNT(file_url) AS count_with_file_url,
        ROUND(AVG(row_count) FILTER (WHERE row_count IS NOT NULL), 2) AS avg_row_count
      FROM data_exports
      GROUP BY status
      ORDER BY export_count DESC, status ASC
    • The warehouse answers
      Submitted to tonight's batch. Your work on this request is done.
    • ViktorSubmitted. The report groups `data_exports` by its literal `status` value (no assumption about which string means "done"), with counts, date range, and how often `completed_at`/`file_url` are actually populated per status — which will directly show whether "completed" is just a naming mismatch or a real fulfillment gap.