<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Re: How to Track Hourly or Daily # of Upsert/Delete Metrics in a DLT Streaming Pipeline in Data Engineering</title>
    <link>https://community.databricks.com/t5/data-engineering/how-to-track-hourly-or-daily-of-upsert-delete-metrics-in-a-dlt/m-p/144594#M52347</link>
    <description>&lt;P&gt;Hi&amp;nbsp;&lt;a href="https://community.databricks.com/t5/user/viewprofilepage/user-id/210085"&gt;@_its_akshaye&lt;/a&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="p1"&gt;Yes—capture it from the DLT event log and derive it directly from the target table’s CDF, then aggregate by time.&lt;/P&gt;
&lt;P class="p2"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="p3"&gt;&lt;STRONG&gt;Options that work well&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="ul1"&gt;
&lt;LI class="li1"&gt;Use the DLT event log “rows written” metrics&lt;/LI&gt;
&lt;UL class="ul1"&gt;
&lt;LI class="li1"&gt;Every pipeline writes a structured event log to your pipeline storage/UC TVF that includes per-table batch stats such as rows written/processed per update. You can query it and bucket by hour/day to get upsert/delete counts over time.&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI class="li1"&gt;Derive metrics from the Silver table’s Change Data Feed (CDF)&lt;/LI&gt;
&lt;UL class="ul1"&gt;
&lt;LI class="li1"&gt;CDF exposes row-level changes with _change_type values like insert, delete, update_preimage, update_postimage, which you can group by a timestamp column (for example, your sequencing column) to produce hourly/daily counts of inserts/updates/deletes that actually landed in Silver.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/UL&gt;
&lt;P class="p3"&gt;&lt;STRONG&gt;Example queries&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="p1"&gt;Below are templates you can run as-is after replacing identifiers; they produce hourly metrics. Switch date_trunc('hour', ...) to 'day' for daily.&lt;/P&gt;
&lt;P class="p1"&gt;1) From the DLT event log&lt;/P&gt;
&lt;UL class="ul1"&gt;
&lt;LI class="li1"&gt;If your pipeline is in Unity Catalog, use the TVF: SQL: SELECT date_trunc('hour', timestamp) AS hour_bucket, details:flow_progress.metrics.num_output_rows::bigint AS rows_written, details:flow_progress.output_dataset AS output_dataset FROM event_log('&amp;lt;pipeline_id&amp;gt;') WHERE event_type = 'flow_progress' AND details:flow_progress.metrics.num_output_rows IS NOT NULL QUALIFY output_dataset = '&amp;lt;your_silver_table_uc_name&amp;gt;' GROUP BY 1, 3 ORDER BY 1;&lt;/LI&gt;
&lt;LI class="li1"&gt;If your pipeline uses HMS storage paths, read the Delta table at system/events: SQL: SELECT date_trunc('hour', timestamp) AS hour_bucket, details:flow_progress.metrics.num_output_rows::bigint AS rows_written, details:flow_progress.output_dataset AS output_dataset FROM delta.'dbfs:/pipelines/&amp;lt;pipeline-id&amp;gt;/system/events' WHERE event_type = 'flow_progress' AND details:flow_progress.metrics.num_output_rows IS NOT NULL AND details:flow_progress.output_dataset = '&amp;lt;your_silver_table_uc_name&amp;gt;' GROUP BY 1, 3 ORDER BY 1;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="p1"&gt;Notes:&lt;/P&gt;
&lt;UL class="ul1"&gt;
&lt;LI class="li1"&gt;flow_progress carries structured metrics for each table update; num_output_rows aligns with what the UI shows and is easy to roll up by time buckets.&lt;/LI&gt;
&lt;LI class="li1"&gt;If you need to separate updates vs deletes precisely, the event log provides totals written per output dataset, but not semantically split by operation type. For that breakdown, use CDF (next option).&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="p1"&gt;2) From the Silver table’s CDF (semantic split: insert/update/delete)&lt;/P&gt;
&lt;P class="p1"&gt;SQL: SELECT date_trunc('hour', COALESCE(_commit_timestamp, &amp;lt;your_event_ts&amp;gt;)) AS hour_bucket, SUM(CASE WHEN _change_type = 'insert' THEN 1 ELSE 0 END) AS inserts, SUM(CASE WHEN _change_type = 'delete' THEN 1 ELSE 0 END) AS deletes, SUM(CASE WHEN _change_type IN ('update_postimage') THEN 1 ELSE 0 END) AS updates FROM table_changes('&amp;lt;catalog&amp;gt;.&amp;lt;schema&amp;gt;.&amp;lt;silver_table&amp;gt;', START =&amp;gt; '2024-12-01') GROUP BY 1 ORDER BY 1;&lt;/P&gt;
&lt;P class="p4"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="p1"&gt;&lt;STRONG&gt;Tips:&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="ul1"&gt;
&lt;LI class="li1"&gt;_commit_timestamp exists on modern runtimes; otherwise use your sequencing column (e.g., operation_date) as the time reference.&lt;/LI&gt;
&lt;LI class="li1"&gt;For updates, you typically count update_postimage rows; update_preimage rows represent the before-image and usually aren’t counted as “applied rows.”&lt;/LI&gt;
&lt;LI class="li1"&gt;Retention: CDF is subject to the table’s retention policy and VACUUM; keep that in mind for long lookbacks.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="p3"&gt;&lt;STRONG&gt;When to use which&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="ul1"&gt;
&lt;LI class="li1"&gt;Need exact operation split (insert/update/delete) at Silver: use CDF on the Silver table and aggregate by hour/day.&lt;/LI&gt;
&lt;LI class="li1"&gt;Need a lightweight, “what was written per batch” view that mirrors the DLT UI: query event_log flow_progress and bucket by time (hour/day). &lt;BR /&gt;&lt;BR /&gt;&lt;/LI&gt;
&lt;/UL&gt;</description>
    <pubDate>Tue, 20 Jan 2026 15:14:58 GMT</pubDate>
    <dc:creator>Saritha_S</dc:creator>
    <dc:date>2026-01-20T15:14:58Z</dc:date>
    <item>
      <title>How to Track Hourly or Daily # of Upsert/Delete Metrics in a DLT Streaming Pipeline</title>
      <link>https://community.databricks.com/t5/data-engineering/how-to-track-hourly-or-daily-of-upsert-delete-metrics-in-a-dlt/m-p/144553#M52337</link>
      <description>&lt;P&gt;We created a Delta Live Tables (DLT) streaming pipeline to ingest data from the Bronze layer to the Silver layer using Change Data Feed (CDF) enabled.&lt;/P&gt;&lt;P&gt;The stream runs continuously and shows # of upserted and deleted rows at an aggregate level from the time it started. Is there a way to capture these metrics at a daily or hourly level to better understand the ingestion load on the stream?&lt;/P&gt;</description>
      <pubDate>Tue, 20 Jan 2026 11:36:27 GMT</pubDate>
      <guid>https://community.databricks.com/t5/data-engineering/how-to-track-hourly-or-daily-of-upsert-delete-metrics-in-a-dlt/m-p/144553#M52337</guid>
      <dc:creator>_its_akshaye</dc:creator>
      <dc:date>2026-01-20T11:36:27Z</dc:date>
    </item>
    <item>
      <title>Re: How to Track Hourly or Daily # of Upsert/Delete Metrics in a DLT Streaming Pipeline</title>
      <link>https://community.databricks.com/t5/data-engineering/how-to-track-hourly-or-daily-of-upsert-delete-metrics-in-a-dlt/m-p/144594#M52347</link>
      <description>&lt;P&gt;Hi&amp;nbsp;&lt;a href="https://community.databricks.com/t5/user/viewprofilepage/user-id/210085"&gt;@_its_akshaye&lt;/a&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="p1"&gt;Yes—capture it from the DLT event log and derive it directly from the target table’s CDF, then aggregate by time.&lt;/P&gt;
&lt;P class="p2"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="p3"&gt;&lt;STRONG&gt;Options that work well&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="ul1"&gt;
&lt;LI class="li1"&gt;Use the DLT event log “rows written” metrics&lt;/LI&gt;
&lt;UL class="ul1"&gt;
&lt;LI class="li1"&gt;Every pipeline writes a structured event log to your pipeline storage/UC TVF that includes per-table batch stats such as rows written/processed per update. You can query it and bucket by hour/day to get upsert/delete counts over time.&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI class="li1"&gt;Derive metrics from the Silver table’s Change Data Feed (CDF)&lt;/LI&gt;
&lt;UL class="ul1"&gt;
&lt;LI class="li1"&gt;CDF exposes row-level changes with _change_type values like insert, delete, update_preimage, update_postimage, which you can group by a timestamp column (for example, your sequencing column) to produce hourly/daily counts of inserts/updates/deletes that actually landed in Silver.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/UL&gt;
&lt;P class="p3"&gt;&lt;STRONG&gt;Example queries&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class="p1"&gt;Below are templates you can run as-is after replacing identifiers; they produce hourly metrics. Switch date_trunc('hour', ...) to 'day' for daily.&lt;/P&gt;
&lt;P class="p1"&gt;1) From the DLT event log&lt;/P&gt;
&lt;UL class="ul1"&gt;
&lt;LI class="li1"&gt;If your pipeline is in Unity Catalog, use the TVF: SQL: SELECT date_trunc('hour', timestamp) AS hour_bucket, details:flow_progress.metrics.num_output_rows::bigint AS rows_written, details:flow_progress.output_dataset AS output_dataset FROM event_log('&amp;lt;pipeline_id&amp;gt;') WHERE event_type = 'flow_progress' AND details:flow_progress.metrics.num_output_rows IS NOT NULL QUALIFY output_dataset = '&amp;lt;your_silver_table_uc_name&amp;gt;' GROUP BY 1, 3 ORDER BY 1;&lt;/LI&gt;
&lt;LI class="li1"&gt;If your pipeline uses HMS storage paths, read the Delta table at system/events: SQL: SELECT date_trunc('hour', timestamp) AS hour_bucket, details:flow_progress.metrics.num_output_rows::bigint AS rows_written, details:flow_progress.output_dataset AS output_dataset FROM delta.'dbfs:/pipelines/&amp;lt;pipeline-id&amp;gt;/system/events' WHERE event_type = 'flow_progress' AND details:flow_progress.metrics.num_output_rows IS NOT NULL AND details:flow_progress.output_dataset = '&amp;lt;your_silver_table_uc_name&amp;gt;' GROUP BY 1, 3 ORDER BY 1;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="p1"&gt;Notes:&lt;/P&gt;
&lt;UL class="ul1"&gt;
&lt;LI class="li1"&gt;flow_progress carries structured metrics for each table update; num_output_rows aligns with what the UI shows and is easy to roll up by time buckets.&lt;/LI&gt;
&lt;LI class="li1"&gt;If you need to separate updates vs deletes precisely, the event log provides totals written per output dataset, but not semantically split by operation type. For that breakdown, use CDF (next option).&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="p1"&gt;2) From the Silver table’s CDF (semantic split: insert/update/delete)&lt;/P&gt;
&lt;P class="p1"&gt;SQL: SELECT date_trunc('hour', COALESCE(_commit_timestamp, &amp;lt;your_event_ts&amp;gt;)) AS hour_bucket, SUM(CASE WHEN _change_type = 'insert' THEN 1 ELSE 0 END) AS inserts, SUM(CASE WHEN _change_type = 'delete' THEN 1 ELSE 0 END) AS deletes, SUM(CASE WHEN _change_type IN ('update_postimage') THEN 1 ELSE 0 END) AS updates FROM table_changes('&amp;lt;catalog&amp;gt;.&amp;lt;schema&amp;gt;.&amp;lt;silver_table&amp;gt;', START =&amp;gt; '2024-12-01') GROUP BY 1 ORDER BY 1;&lt;/P&gt;
&lt;P class="p4"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="p1"&gt;&lt;STRONG&gt;Tips:&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="ul1"&gt;
&lt;LI class="li1"&gt;_commit_timestamp exists on modern runtimes; otherwise use your sequencing column (e.g., operation_date) as the time reference.&lt;/LI&gt;
&lt;LI class="li1"&gt;For updates, you typically count update_postimage rows; update_preimage rows represent the before-image and usually aren’t counted as “applied rows.”&lt;/LI&gt;
&lt;LI class="li1"&gt;Retention: CDF is subject to the table’s retention policy and VACUUM; keep that in mind for long lookbacks.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P class="p3"&gt;&lt;STRONG&gt;When to use which&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL class="ul1"&gt;
&lt;LI class="li1"&gt;Need exact operation split (insert/update/delete) at Silver: use CDF on the Silver table and aggregate by hour/day.&lt;/LI&gt;
&lt;LI class="li1"&gt;Need a lightweight, “what was written per batch” view that mirrors the DLT UI: query event_log flow_progress and bucket by time (hour/day). &lt;BR /&gt;&lt;BR /&gt;&lt;/LI&gt;
&lt;/UL&gt;</description>
      <pubDate>Tue, 20 Jan 2026 15:14:58 GMT</pubDate>
      <guid>https://community.databricks.com/t5/data-engineering/how-to-track-hourly-or-daily-of-upsert-delete-metrics-in-a-dlt/m-p/144594#M52347</guid>
      <dc:creator>Saritha_S</dc:creator>
      <dc:date>2026-01-20T15:14:58Z</dc:date>
    </item>
    <item>
      <title>Hi @_its_akshaye, The pipeline event log captures exactly...</title>
      <link>https://community.databricks.com/t5/data-engineering/how-to-track-hourly-or-daily-of-upsert-delete-metrics-in-a-dlt/m-p/150288#M53335</link>
      <description>&lt;P&gt;Hi &lt;a href="https://community.databricks.com/t5/user/viewprofilepage/user-id/210085"&gt;@_its_akshaye&lt;/a&gt;,&lt;/P&gt;
&lt;P&gt;The pipeline event log captures exactly what you need. Every Lakeflow Spark Declarative Pipeline (SDP, formerly known as DLT) records flow_progress events that include per-flow metrics with num_upserted_rows and num_deleted_rows fields. You can query these and bucket them by hour or day.&lt;/P&gt;
&lt;P&gt;OPTION 1: QUERY THE EVENT LOG (RECOMMENDED)&lt;/P&gt;
&lt;P&gt;The event log's flow_progress events contain a metrics object with these fields:&lt;/P&gt;
&lt;PRE&gt;num_output_rows    - total output rows written
num_upserted_rows  - rows upserted into the target
num_deleted_rows   - rows deleted from the target&lt;/PRE&gt;
&lt;P&gt;If your pipeline is registered in Unity Catalog, use the event_log() table-valued function:&lt;/P&gt;
&lt;PRE&gt;SELECT
date_trunc('hour', timestamp) AS hour_bucket,
details:flow_progress.metrics.num_upserted_rows::bigint AS upserted_rows,
details:flow_progress.metrics.num_deleted_rows::bigint AS deleted_rows,
details:flow_progress.metrics.num_output_rows::bigint AS total_output_rows,
details:flow_progress.flow_name AS flow_name
FROM event_log('&amp;lt;your_pipeline_id&amp;gt;')
WHERE event_type = 'flow_progress'
AND details:flow_progress.status = 'COMPLETED'
GROUP BY 1, 2, 3, 4, 5
ORDER BY 1&lt;/PRE&gt;
&lt;P&gt;Replace '&amp;lt;your_pipeline_id&amp;gt;' with either your pipeline ID or the fully qualified table name if you have published the event log. Switch 'hour' to 'day' for daily granularity.&lt;/P&gt;
&lt;P&gt;For Hive metastore pipelines (not Unity Catalog), you can read the event log directly as a Delta table:&lt;/P&gt;
&lt;PRE&gt;SELECT
date_trunc('hour', timestamp) AS hour_bucket,
details:flow_progress.metrics.num_upserted_rows::bigint AS upserted_rows,
details:flow_progress.metrics.num_deleted_rows::bigint AS deleted_rows,
details:flow_progress.metrics.num_output_rows::bigint AS total_output_rows,
details:flow_progress.flow_name AS flow_name
FROM delta.`dbfs:/pipelines/&amp;lt;pipeline-id&amp;gt;/system/events`
WHERE event_type = 'flow_progress'
AND details:flow_progress.status = 'COMPLETED'
GROUP BY 1, 2, 3, 4, 5
ORDER BY 1&lt;/PRE&gt;
&lt;P&gt;To aggregate into true hourly/daily totals (summing across multiple micro-batches), wrap the query:&lt;/P&gt;
&lt;PRE&gt;SELECT
hour_bucket,
flow_name,
SUM(upserted_rows) AS total_upserted,
SUM(deleted_rows) AS total_deleted,
SUM(total_output_rows) AS total_output
FROM (
SELECT
  date_trunc('hour', timestamp) AS hour_bucket,
  details:flow_progress.metrics.num_upserted_rows::bigint AS upserted_rows,
  details:flow_progress.metrics.num_deleted_rows::bigint AS deleted_rows,
  details:flow_progress.metrics.num_output_rows::bigint AS total_output_rows,
  details:flow_progress.flow_name AS flow_name
FROM event_log('&amp;lt;your_pipeline_id&amp;gt;')
WHERE event_type = 'flow_progress'
  AND details:flow_progress.status = 'COMPLETED'
)
GROUP BY hour_bucket, flow_name
ORDER BY hour_bucket&lt;/PRE&gt;
&lt;P&gt;OPTION 2: QUERY CHANGE DATA FEED ON THE SILVER TABLE&lt;/P&gt;
&lt;P&gt;If you also want to see the semantic breakdown of inserts vs. updates vs. deletes from the target table's perspective, you can query the Silver table's Change Data Feed directly using table_changes():&lt;/P&gt;
&lt;PRE&gt;SELECT
date_trunc('hour', _commit_timestamp) AS hour_bucket,
SUM(CASE WHEN _change_type = 'insert' THEN 1 ELSE 0 END) AS inserts,
SUM(CASE WHEN _change_type = 'update_postimage' THEN 1 ELSE 0 END) AS updates,
SUM(CASE WHEN _change_type = 'delete' THEN 1 ELSE 0 END) AS deletes
FROM table_changes('&amp;lt;catalog&amp;gt;.&amp;lt;schema&amp;gt;.&amp;lt;silver_table&amp;gt;', 1)
GROUP BY 1
ORDER BY 1&lt;/PRE&gt;
&lt;P&gt;Notes on CDF:&lt;BR /&gt;
- Count update_postimage for updates (update_preimage is the before-image).&lt;BR /&gt;
- The second argument can be a version number or a timestamp string.&lt;BR /&gt;
- CDF data is subject to the table's data retention and VACUUM settings, so keep that in mind for long lookback periods.&lt;/P&gt;
&lt;P&gt;WHICH APPROACH TO USE&lt;/P&gt;
&lt;P&gt;- Event log (Option 1): lightweight, gives you batch-level throughput numbers that match what the pipeline UI shows. Best for monitoring ingestion load over time.&lt;BR /&gt;
- CDF (Option 2): gives you the exact semantic split of insert/update/delete at the row level from the target table. Best when you need precise operation-type breakdowns.&lt;/P&gt;
&lt;P&gt;You can combine both for a complete picture.&lt;/P&gt;
&lt;P&gt;DOCUMENTATION REFERENCES&lt;/P&gt;
&lt;P&gt;Event log schema (includes the full FlowMetrics specification):&lt;BR /&gt;
&lt;A href="https://docs.databricks.com/aws/en/ldp/monitor-event-log-schema" target="_blank"&gt;https://docs.databricks.com/aws/en/ldp/monitor-event-log-schema&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;Querying the event log:&lt;BR /&gt;
&lt;A href="https://docs.databricks.com/aws/en/ldp/monitor-event-logs" target="_blank"&gt;https://docs.databricks.com/aws/en/ldp/monitor-event-logs&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;Change Data Feed:&lt;BR /&gt;
&lt;A href="https://docs.databricks.com/aws/en/delta/delta-change-data-feed.html" target="_blank"&gt;https://docs.databricks.com/aws/en/delta/delta-change-data-feed.html&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;* This reply used an agent system I built to research and draft this response based on the wide set of documentation I have available and previous memory. I personally review the draft for any obvious issues and for monitoring system reliability and update it when I detect any drift, but there is still a small chance that something is inaccurate, especially if you are experimenting with brand new features.&lt;/P&gt;
&lt;P&gt;If this answer resolves your question, could you mark it as "Accept as Solution"? That helps other users quickly find the correct fix.&lt;/P&gt;</description>
      <pubDate>Mon, 09 Mar 2026 01:04:00 GMT</pubDate>
      <guid>https://community.databricks.com/t5/data-engineering/how-to-track-hourly-or-daily-of-upsert-delete-metrics-in-a-dlt/m-p/150288#M53335</guid>
      <dc:creator>SteveOstrowski</dc:creator>
      <dc:date>2026-03-09T01:04:00Z</dc:date>
    </item>
  </channel>
</rss>

