CertSafari
    Databricks Certified Data Engineer Professional - October 9 onwards· Lessons

    Domain 4 · Lesson 22/46

    Cost Analysis with system.billing.usage and Compute System Tables

    Use Databricks system tables (billing, compute, access, lakeflow) for cost analysis, auditing, and workload monitoring.

    10 min read
    2% of exam
    4 sources
    Published 3 Oct 2026
    Docs as of 30 Sep 2026

    What you will be able to do

    • Explain what Databricks system tables are, how access to them is granted, and what they cost to query
    • Attribute DBU usage in system.billing.usage to jobs, pipelines, warehouses and identities using usage_metadata, identity_metadata and billing_origin_product
    • Write usage aggregations that stay correct when Databricks issues RETRACTION and RESTATEMENT records, and convert DBUs to list cost
    • Cross-reference billing records with system.compute tables to attribute cost to clusters, owners and node roles

    Key concept

    System tables — A read-only, Databricks-hosted set of tables in the system catalog that records your account's operational data: billing, compute, audit and job activity. Unity Catalog governs them and you query them with SQL, so cost analysis, auditing and workload monitoring all come down to joining these tables.

    1.What system tables are and who can read them

    Cost questions, security questions and job-health questions in Databricks usually have the same answer: query the system catalog. System tables are a Databricks-hosted analytical store that the documentation says you can use to monitor costs, audit security events, track compute and job performance, and observe data and AI workloads. Each schema has its own job. system.billing covers cost, system.compute covers cluster and warehouse configuration and utilization, system.access covers audit events, and system.lakeflow covers jobs and pipelines.

    Unity Catalog governs access. Your account needs at least one Unity Catalog-enabled workspace, and the tables can only be read from one. They still contain data from every workspace in the account, including workspaces that don't use Unity Catalog. They are read-only. Users who hold both the account admin and metastore admin roles can read them by default. Everyone else needs three grants from an admin: USE CATALOG on system, plus USE SCHEMA and SELECT on the schemas they need.

    Scope also depends on region. Most tables hold data for the workspaces in your metastore's region. system.billing.usage and system.billing.list_prices are global, so you can see account-wide spend from any region.

    Checkpoint 1 of 5· Check yourself

    A data engineer who is neither an account admin nor a metastore admin needs to query system.billing.usage. Which set of privileges must an admin grant?

    Sources1

    2.Attributing spend with system.billing.usage

    system.billing.usage has one row for each piece of billable usage. A row records a quantity of units (usually DBUs) for a SKU over a time window, plus metadata showing what generated it. Cost analysis means grouping these rows along one of the columns below.

    system.billing.usage columns you group and filter by
    ColumnWhat it tells you
    usage_dateDate of the record; use it for faster aggregation by date
    sku_nameThe SKU billed, e.g. STANDARD_ALL_PURPOSE_COMPUTE
    usage_quantity / usage_unitHow many units (e.g. DBU) were consumed
    usage_metadataIDs of the resources involved: cluster_id, job_id, job_run_id, warehouse_id, dlt_pipeline_id, notebook_id
    identity_metadatarun_as, owned_by (SQL warehouses), created_by (Apps, agents)
    billing_origin_productThe product that originated the usage, e.g. JOBS, DLT, SQL, ALL_PURPOSE
    product_featuresFeature details such as is_serverless, is_photon, jobs_tier, dlt_tier
    custom_tagsYour custom tags, e.g. an env tag
    record_typeORIGINAL, RETRACTION or RESTATEMENT

    Each record populates only some of the usage_metadata fields, depending on the compute type. The difference that matters most is that job_id and job_run_id are filled in for serverless jobs and for jobs on job compute, but not for jobs that run on all-purpose compute. Usage from a job running on all-purpose compute has a cluster_id and no job_id, so a cost-per-job report built on job_id will miss it. In the same way, dlt_pipeline_id and dlt_update_id identify Lakeflow pipeline usage, including features built on pipelines such as materialized views and Lakeflow Connect.

    SKUs alone can mislead. Several products bill under the same SKU: data quality monitoring, predictive optimization and serverless workflows all bill under the serverless jobs SKU. To tell them apart, group by billing_origin_product and product_features instead of sku_name.

    Checkpoint 2 of 5· Match them up

    Match each billing_origin_product value to the usage it represents

    Tap a term, then the definition that fits it.

    Checkpoint 3 of 5· Exam question

    A FinOps engineer must attribute each dollar of Databricks Jobs compute to the specific job run that generated it, joining `system.billing.usage` with `system.lakeflow.job_run_timeline`. The engineer starts from this skeleton: ```sql SELECT u.usage_quantity, u.sku_name, rt.job_id, rt.run_id FROM system.billing.usage u JOIN system.lakeflow.job_run_timeline rt ON <JOIN_CONDITION> ``` Which `<JOIN_CONDITION>` correctly attributes each usage record to the job run that produced it?

    Sources2

    3.Corrections, record availability and turning DBUs into cost

    system.billing.usage never edits rows. When a field is wrong, Databricks appends two records. The first is a RETRACTION, identical to the original except that usage_quantity is negated. The second is a RESTATEMENT with the corrected values. If the original record should never have existed, only a retraction is added. A correct total therefore has to sum across all record types. Filtering to ORIGINAL alone double-counts nothing but keeps the wrong values.

    Timing matters for dashboards as well. Records typically arrive within 12 hours, but that applies only to ORIGINAL records. Retractions and restatements can arrive later, so recent totals can still change.

    Correction-safe hourly usage per job: summing makes each retraction cancel its originalsql
    SELECT
      usage_metadata.job_id, usage_start_time, usage_end_time,
      SUM(usage_quantity) as usage_quantity
    FROM system.billing.usage
    GROUP BY ALL
    HAVING usage_quantity != 0

    Checkpoint 4 of 5· Fill the gap

    Which aggregate makes this query return corrected usage even after retractions and restatements?

    SELECT
      usage_metadata.job_id, usage_start_time, usage_end_time,
       ? (usage_quantity) as usage_quantity
    FROM system.billing.usage
    GROUP BY ALL
    HAVING usage_quantity != 0

    DBUs aren't money. To get list cost, join system.billing.usage to system.billing.list_prices, which keeps a historical log of SKU pricing with a new row each time a price changes. Databricks' job-cost example joins on cloud and sku_name and requires usage_start_time to fall inside the price's price_start_time/price_end_time window. A null price_end_time marks the current price. The example then multiplies usage_quantity by pricing.default to get list_cost, and filters billing_origin_product = 'JOBS' to keep only job spend.

    Sources231

    4.Attributing cost to clusters, owners and nodes with system.compute

    For classic compute, a billing row gives you a cluster_id and a node_type, but node_type doesn't say whether the node was a driver or a worker. If the driver and workers use the same instance type, their usage is combined into one record. If they use different types, the usage appears as separate records. To work out which role cost what, join usage_metadata.cluster_id to the compute tables. system.compute.clusters records each cluster's driver_node_type and worker_node_type, and system.compute.node_timeline has a driver boolean for every node.

    Identity has a similar subtlety. On all-purpose compute, identity_metadata.run_as is the identity that created the compute resource, which isn't necessarily its owner. To charge usage back to owners, join to system.compute.clusters.owned_by.

    Compute system tables used alongside billing data
    TableWhat it records
    system.compute.clustersSlow-changing dimension table with the full history of compute configurations for any cluster, including driver_node_type, worker_node_type, owned_by
    system.compute.node_timelineUtilization metrics of all-purpose and jobs compute: driver, cpu_user_percent, cpu_wait_percent, mem_used_percent (90-day free retention)
    system.compute.warehousesFull history of configurations for any SQL warehouse
    system.compute.warehouse_eventsSQL warehouse events such as starting, stopping, scaling up and down

    node_timeline is also where cost meets efficiency. Its CPU and memory percentages show whether a costly cluster was actually busy. A high cpu_wait_percent means the CPU spent its time waiting for I/O. Low mem_used_percent on a large node type suggests the cluster is oversized.

    Checkpoint 5 of 5· Check yourself

    You build a chargeback report that groups all-purpose compute usage by identity_metadata.run_as. A manager complains that the costs aren't landing on the cluster owners. What fixes the report?

    Sources214

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.usage_metadata.job_id is populated for every job, so grouping by job_id captures all job spend.Why is that wrong?

      job_id and job_run_id are populated only for serverless jobs and jobs on job compute. Jobs on all-purpose compute have only a cluster_id.

      Covered in Attributing spend with system.billing.usage

    2. 2.Every billing record, corrections included, arrives within 12 hours, so yesterday's totals are final.Why is that wrong?

      The 12-hour availability applies only to ORIGINAL records. Retractions and restatements can arrive later and change totals that were already summed.

      Covered in Corrections, record availability and turning DBUs into cost

    3. 3.identity_metadata.run_as on all-purpose compute identifies the cluster owner.Why is that wrong?

      On all-purpose compute, run_as is the identity that created the compute resource. For the owner, join to system.compute.clusters.owned_by.

      Covered in Attributing cost to clusters, owners and nodes with system.compute

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “Use them to monitor costs, audit security events, track compute and job performance, and observe data and AI workloads.”
      ↩︎ What system tables are and who can read them
      “System tables include data from all workspaces in your account, but they can only be accessed from a Unity Catalog-enabled workspace.”
      ↩︎ What system tables are and who can read them
      “Users with both the account admin and metastore admin roles have access to system tables by default.”
      ↩︎ What system tables are and who can read them
      “System tables are read-only and cannot be modified.”
      ↩︎ What system tables are and who can read them
      “A historical log of SKU pricing. A record gets added each time there is a change to a SKU price.”
      ↩︎ Corrections, record availability and turning DBUs into cost
      “Captures the utilization metrics of your all-purpose and jobs compute resources.”
      ↩︎ Attributing cost to clusters, owners and nodes with system.compute
      “System tables provide a Databricks-hosted analytical store of your account's operational data.”
      ↩︎ Key concept
      “System tables are free to use. You are only charged for the compute used to query them.”
      ↩︎ Prediction
      “USE CATALOG on the system catalog, USE SCHEMA on the system schemas, and SELECT on the system schemas.”
      ↩︎ Checkpoint
    2. 2.
      “Serverless jobs and jobs run on job compute (does not populate for jobs run on all-purpose compute)”
      ↩︎ Attributing spend with system.billing.usage
      “data quality monitoring, predictive optimization, and serverless workflows are all billed under the same serverless jobs SKU.”
      ↩︎ Attributing spend with system.billing.usage
      “A retraction record negates the original incorrect record, then a restatement record includes the corrected information.”
      ↩︎ Corrections, record availability and turning DBUs into cost
      “Records are typically available within 12 hours.”
      ↩︎ Corrections, record availability and turning DBUs into cost
      “To attribute usage to a specific node role, you can cross-reference usage_metadata.cluster_id in the usage table with data from the compute system tables.”
      ↩︎ Attributing cost to clusters, owners and nodes with system.compute
      “Serverless jobs and jobs run on job compute (does not populate for jobs run on all-purpose compute)”
      ↩︎ Exam trap 1
      “This availability applies only to records where record_type is ORIGINAL.”
      ↩︎ Exam trap 2
      “The identity that created the compute resource. This is not necessarily the compute owner.”
      ↩︎ Exam trap 3
      “the billing_origin_product and product_features columns provide more insight into the specific product and features associated with the usage”
      ↩︎ Checkpoint
      “To attribute usage to the owner, join to system.compute.clusters.owned_by using usage_metadata.cluster_id.”
      ↩︎ Checkpoint

    Continue to page 2 of 2

    Auditing and Job Monitoring with system.access.audit and system.lakeflow

    Spotted a mistake, or was something unclear? Tell us.