Approach: use a window function (ROW_NUMBER or FIRST_VALUE) partitioned by event_id to pick the earliest or latest event_time per event_id, then upsert the deduped set into a partitioned target table to enforce idempotency.Example: keep earliest occurrence per event_id, dedupe in a staging table (staged_events) then MERGE into partitioned events table (events_table partitioned by event_date).sql
-- 1) Build deduped set in staging using ROW_NUMBER to keep earliest event_time
WITH dedup AS (
SELECT
event_id,
event_time,
payload,
event_date, -- derived partition key (e.g. date(event_time))
ROW_NUMBER() OVER (PARTITION BY event_id ORDER BY event_time ASC, payload) AS rn
FROM staged_events
)
SELECT * FROM dedup WHERE rn = 1;
Upsert into target (ANSI MERGE pattern; adapt to your DB: MERGE for Snowflake/SQL Server/BigQuery (via DML), INSERT...ON CONFLICT for Postgres):sql
MERGE INTO events_table tgt
USING (
SELECT event_id, event_time, payload, event_date FROM dedup WHERE rn = 1
) src
ON tgt.event_id = src.event_id
AND tgt.event_date = src.event_date -- ensure partition-scoped idempotency
WHEN MATCHED AND src.event_time < tgt.event_time THEN
UPDATE SET event_time = src.event_time, payload = src.payload
WHEN NOT MATCHED THEN
INSERT (event_id, event_time, payload, event_date)
VALUES (src.event_id, src.event_time, src.payload, src.event_date);
Notes & trade-offs:- Ingestion-time dedupe (above) reduces storage, downstream compute, and simplifies consumers; requires more complexity/latency in the ingestion pipeline and needs accurate keys and clocks.- Downstream dedupe (on read/analytics) is simpler at ingest and more tolerant of out-of-order arrivals, but duplicates propagate to storage and can cause higher egress/compute costs and inconsistent metrics until deduped.- Consider hybrid: lightweight ingestion dedupe (filter obvious duplicates e.g., identical event_id+hash within short window) + downstream deterministic dedupe (window functions) to handle late arrivals.- Ensure idempotent producers (idempotency keys), choose earliest vs latest based on business semantics, and account for late-arriving updates with watermarking or TTL for reprocessing.