Interview Q&A

๐Ÿงฑ Databricks

Delta, Unity Catalog, jobs, and lakehouse production.

92 questions ยท 50 theory ยท 42 coding

Q-DBX-001 What is Databricks? theory easy

Answer

Databricks is a managed lakehouse platform. It runs Apache Spark compute on your cloud account and adds governance, SQL, workflows, and notebooks. You store data as Delta tables and process it with jobs, pipelines, or SQL warehouses.

Explanation

Interviewers want the product, not a Spark lecture. Say compute, data, and governance as three parts. Spark still does the work. Databricks adds Unity Catalog, Photon, serverless, Auto Loader, and Jobs. You still pay cloud VMs or serverless usage plus Databricks DBUs. Production teams treat it as a platform with identity, cost tags, and audit, not as a single cluster.

Trap

Saying Databricks is only a notebook UI or only a Spark hosting service.

Q-DBX-002 What is a lakehouse? theory easy

Answer

A lakehouse keeps data in cheap object storage and still gives warehouse features. You get ACID tables, schema, time travel, and SQL on the same files. Delta Lake is the usual table format on Databricks.

Explanation

A data lake stores files with weak quality and weak transactions. A warehouse is fast and governed but often copies data out of the lake. A lakehouse tries to do both: open files in the lake, plus table contracts. On Databricks that contract is Delta, governed by Unity Catalog. Cost stays lower when you do not copy the same data into a second warehouse. Governance still matters, because a lake of ungoverned files is not a lakehouse.

Trap

Saying a lakehouse is just a data lake with a SQL endpoint.

Q-DBX-003 What is Unity Catalog and what is the object hierarchy? theory easy

Answer

Unity Catalog is the governance layer for data, AI assets, and files. The hierarchy is metastore, then catalog, then schema, then objects. Objects include tables, views, volumes, functions, and models.

Explanation

Think of the metastore as the region-level registry, usually one per cloud region. Catalogs often map to environments or domains, such as prod or sales. Schemas group tables for a team. You name a table as catalog.schema.table. Privileges inherit down this tree. Lineage and audit sit on the same objects. Production access should use groups and service principals, not one shared admin user.

Trap

Describing only database.table Hive names and skipping catalog, volumes, and privileges.

Q-DBX-004 What is the three-level namespace? theory easy

Answer

Every Unity Catalog table is catalog.schema.table. The catalog is the top name. The schema is the group inside it. The table is the data object.

Explanation

Hive Metastore used two levels: database.table. That made prod and dev easy to mix. Three levels let you separate prod.sales.orders from dev.sales.orders. SQL, Python, and BI tools should use the full name. Cost and governance both improve when catalogs are split by environment. Do not rely on a default catalog in production jobs.

Trap

Saying the first part is the workspace name or the storage account name.

Q-DBX-005 Volumes vs tables โ€” when do you use each? theory easy

Answer

Tables store rows with a schema. Volumes store files such as CSV, images, models, or landing JSON. Use a table when you query columns. Use a volume when you need governed file paths.

Explanation

A volume lives in a schema, just like a table. The path looks like /Volumes/catalog/schema/volume/folder/file. Auto Loader, COPY INTO, and ML artifacts often read volumes. Do not create a Delta table if the data is unstructured files. Do not dump files into random DBFS folders if Unity Catalog is on. Volumes give GRANT, audit, and lineage-style governance for files. Tables give ACID, CDF, and time travel for rows.

Trap

Calling a volume a table, or using old DBFS mounts for new governed file data.

Q-DBX-006 Managed vs external tables in Unity Catalog theory medium

Answer

A managed table lets Unity Catalog own the data files and the metadata. An external table lets you pick the cloud path. Dropping a managed table is a data-lifecycle event. Dropping an external table usually removes metadata only.

Explanation

Managed tables are the default for new lakehouse work. Predictive optimization and some layout features target Unity Catalog managed tables. External tables are for landing zones you already own, or for sharing a path with another engine. Storage credentials and external locations govern those paths. Current Unity Catalog managed drops keep data for a retention window, often about 8 days, before permanent delete. Hive and older runtimes can differ, so check the workspace docs. Cost: managed is simpler, but you still pay for the cloud files until they are gone.

Trap

Saying DROP TABLE always deletes files immediately for every table type.

Q-DBX-007 What are storage credentials and external locations? theory medium

Answer

A storage credential is the cloud identity that can read a storage account. An external location maps one path to that credential. Tables and volumes then sit on the location. Access is granted on the location, not by handing out cloud keys to users.

Explanation

The chain is credential, then location, then table or volume. The credential is often a managed identity or IAM role. Users get CREATE EXTERNAL TABLE or READ FILES on the location. They should not get the raw cloud secret. This is a governance control and a cost control, because one credential can be scoped to one prefix. If you skip this chain and mount storage with keys, Unity Catalog cannot enforce least privilege.

Trap

Putting storage account keys in a notebook and calling that Unity Catalog security.

Q-DBX-008 Hive Metastore vs Unity Catalog theory medium

Answer

Hive Metastore is workspace-local and two-level. Unity Catalog is account-level, three-level, and built for GRANT, lineage, and audit. New production work should use Unity Catalog.

Explanation

Hive tables can live in a workspace metastore that other workspaces do not share. Privileges are weaker and easy to bypass with paths. Unity Catalog centralizes identity and securables across workspaces attached to the same metastore. You also get volumes, row filters, column masks, and Delta Sharing. Migration is a project: names change, mounts go away, and jobs must run as a service principal. Cost does not drop by itself, but one catalog reduces duplicate copies and shadow tables.

Trap

Saying they are the same thing because both store table names.

Q-DBX-009 How do Unity Catalog privileges inherit? theory medium

Answer

Privileges granted on a catalog or schema flow down to objects inside. You still need USE CATALOG and USE SCHEMA to reach a table. Ownership is separate from GRANT. Least privilege means grant on the smallest object that still works.

Explanation

SELECT on a table is not enough if the user cannot enter the catalog and schema. Teams often grant USE CATALOG and USE SCHEMA to a group, then SELECT only on the gold tables they need. MODIFY is write. ALL PRIVILEGES is rarely needed. Service principals used by jobs should get only what the job writes. Audit who has MANAGE or ownership. Over-granting on the catalog is a common production incident.

Trap

Granting only SELECT on the table and forgetting USE CATALOG and USE SCHEMA.

Q-DBX-010 Row filters and column masks theory medium

Answer

A row filter hides rows. A column mask hides or rewrites column values. Both run inside Unity Catalog, so every SQL client sees the same rule. They are not the same as a view you hope people will use.

Explanation

Row filters implement tenant isolation, such as one airline seeing only its bookings. Masks protect PII, such as email. Policies use SQL functions and group membership. They apply to notebooks, jobs, JDBC, and BI if those tools use Unity Catalog. Shared or standard access mode is required for fine-grained security on a cluster. Test with a non-admin user. There is a cost: filters can block some optimizations, so do not treat them as a substitute for splitting huge tenants into separate tables when scale demands it.

Trap

Saying application code can enforce PII rules and Unity Catalog is optional.

Q-DBX-011 How does Unity Catalog lineage work? theory easy

Answer

Unity Catalog records which tables and columns feed other tables. It captures reads and writes from notebooks, jobs, SQL, and pipelines. You use it for impact analysis and audit, not as a replacement for tests.

Explanation

Open Catalog Explorer and look at the lineage graph. If gold revenue is wrong, walk upstream to silver and bronze. If you rename a silver column, see which gold tables break. Lineage is strongest for Spark SQL and DataFrame reads of UC tables. Some paths, such as raw JDBC in a UDF, may not show up. Governance teams use it with audit logs. It does not by itself stop a bad write.

Trap

Claiming lineage tracks every file and every Python side effect automatically.

Q-DBX-012 Users vs service principals theory medium

Answer

A user is a person. A service principal is a non-human identity for jobs, CI, and tools. Production jobs should run as a service principal. People keep interactive access with their own user.

Explanation

If a job runs as alice@company.com, the job dies or stays privileged when Alice leaves. A service principal has its own grants, secrets access, and audit trail. Git deploy, REST API, and Terraform should use that identity. Grant it least privilege on the catalogs it writes. Cost tags still apply to the job, not to the person. Do not share one "admin SP" across every pipeline.

Trap

Using a personal user or PAT from a laptop as the production job identity.

Q-DBX-013 How should secrets be stored on Databricks? theory easy

Answer

Put secrets in a secret scope. Read them with dbutils.secrets.get. Never print them. Prefer a cloud vault backend and tight ACLs on the scope.

Explanation

Scopes can be Databricks-backed or backed by Azure Key Vault, AWS Secrets Manager, or similar. Jobs and notebooks fetch values at runtime. Cluster environment variables can reference secrets without putting the value in source. Init scripts should not echo secrets. Rotate keys and remove user-level copies. Governance: who can READ the scope is as important as the table GRANT. Cost is not the issue here; leakage is.

Trap

Hard-coding tokens in notebooks, widgets, or Git.

Q-DBX-014 All-purpose clusters vs job clusters theory easy

Answer

All-purpose clusters are for interactive notebooks and shared exploration. Job clusters start for a run and shut down after. Production workflows should use job clusters or serverless jobs. All-purpose DBUs cost more.

Explanation

Leaving an all-purpose cluster on overnight is a classic bill shock. Job clusters isolate libraries and Spark conf per pipeline. They also make retries cleaner because the machine is fresh. Interactive work still needs all-purpose or serverless compute. Use auto-terminate. Tag both kinds for chargeback. Do not "save money" by pinning every team to one giant always-on cluster unless you have measured it.

Trap

Running scheduled production ETL on a shared all-purpose cluster because it is already running.

Q-DBX-015 SQL warehouse vs Spark cluster theory medium

Answer

A SQL warehouse serves Databricks SQL, dashboards, and JDBC/ODBC. A Spark cluster runs notebooks, Python, and jobs. Analysts should hit a warehouse. ETL should hit jobs compute.

Explanation

Warehouses size with T-shirt sizes and auto-stop. Serverless warehouses start fast and scale with query load. Photon is commonly on. You still pay for warehouse uptime, so auto-stop matters. Do not point every Python pipeline at a warehouse. Do not point Power BI at a random all-purpose cluster. Governance is the same Unity Catalog, which is the point: one table, many compute engines.

Trap

Saying a SQL warehouse is just a Spark cluster with a different name and the same pricing.

Q-DBX-016 What is serverless compute on Databricks? theory medium

Answer

Serverless means Databricks manages the machines. You pick a warehouse or job, not a VM type. Startup is faster and scaling is automatic. You still pay for usage.

Explanation

Serverless exists for SQL warehouses, jobs, and notebooks depending on the workspace. You lose some low-level Spark knobs. You gain less ops work and fewer idle VMs. Cost can go up if queries are sloppy, because scale-out is easy. Cost can go down if you used to leave clusters idle. Tag workloads. Set budgets and query limits. Serverless is not a free tier.

Trap

Saying serverless has no cost, or that you can always set every Spark config like a classic cluster.

Q-DBX-017 What is Photon? theory medium

Answer

Photon is Databricks' vectorized C++ engine for Spark SQL and DataFrames. It speeds up scans, joins, aggregations, and Delta I/O. It does not magically speed up every Python UDF or RDD.

Explanation

Photon sits under the same Spark SQL plan. You still write DataFrames. Look at the query profile for Photon operators. There is a DBU multiplier, so faster runtime must beat extra DBU price. Python row-at-a-time UDFs often fall off Photon. Prefer built-in functions. Production advice: turn Photon on for SQL and ETL, measure wall time and cost, and keep UDFs out of the hot path.

Trap

Claiming Photon makes pandas UDFs and RDDs just as fast as SQL.

Q-DBX-018 How does cluster autoscaling work, and what is the cost trap? theory medium

Answer

You set a min and max number of workers. Databricks adds workers when there is backlog and removes them when the cluster is idle. Min workers are always billed. Max workers cap spend and also cap speed.

Explanation

If min equals max, you are not autoscaling. If min is high, you pay for idle capacity. Scale-up helps a sudden shuffle. Scale-down can lag because Spark still holds shuffle files. SQL warehouses autoscale differently, by cluster count. For jobs, a well-sized fixed job cluster can be cheaper than a wide min-max range. Always combine autoscaling with auto-terminate and tags.

Trap

Setting min workers very high "for performance" and then being surprised by the bill.

Q-DBX-019 What are init scripts and when should you use them? theory medium

Answer

An init script runs when a cluster starts, before Spark jobs. Use it only for host-level setup that libraries cannot do. Prefer cluster-scoped scripts stored in a volume. A failing init script fails cluster start.

Explanation

People use init scripts for native packages, drivers, or security agents. Workspace-global init scripts are hard to debug and easy to break every cluster. Unity Catalog volumes are the current place to store the script file. Do not curl random installers from the internet in production. Do not put secrets in the script body. Cluster policies should control who can attach scripts. Libraries and compute images are cleaner when they are enough.

Trap

Using init scripts to pip install every Python library on every start.

Q-DBX-020 What are cluster policies? theory medium

Answer

A cluster policy is a guardrail JSON that limits instance types, Spark conf, tags, and who can create compute. Admins use policies so users cannot launch unbounded machines. Users still create clusters, but inside the fence.

Explanation

Policies can force cost tags, max workers, Photon, Unity Catalog access mode, and auto-terminate. This is both a cost control and a governance control. Without policies, one person can start a huge GPU cluster with no tags. Job compute should use a production policy that requires a service principal and logging. Do not treat policy as optional documentation.

Trap

Saying cluster policies only change the UI defaults and users can ignore them.

Q-DBX-021 Jobs vs Workflows โ€” what is the difference? theory easy

Answer

Workflows is the product name for orchestration in the UI. A job is the actual scheduled unit with tasks, cluster, retries, and alerts. Multi-task jobs form a task DAG. Interviews use both words for the same idea.

Explanation

A job can run a notebook, Python wheel, SQL, dbt, JAR, or pipeline. Tasks can depend on other tasks. You set schedules, timeouts, retries, and email or webhook alerts. Production should use job compute, not an always-on all-purpose cluster. Lakeflow Jobs is newer naming in some docs. The API still says jobs. Version your job definition with Asset Bundles, not clicks only.

Trap

Saying Workflows is a different engine from Jobs, or that a notebook schedule is enough for production.

Q-DBX-022 What is DLT / Lakeflow Spark Declarative Pipelines? theory medium

Answer

DLT is a declarative pipeline product. You declare tables and quality rules. Databricks runs the graph, checkpoints, and retries. Current docs call this Lakeflow Spark Declarative Pipelines. Interviews still say DLT.

Explanation

You write @dlt.table or CREATE STREAMING TABLE plus expectations. Bronze, silver, and gold become a DAG. Streaming and batch can mix. The product manages orchestration that you would otherwise build with jobs plus custom checkpoint code. You still pay for pipeline compute. Expectations can drop, fail, or only warn. Production teams still need tests, alerts, and a service principal. It is not "no-ops Spark."

Trap

Saying DLT is just a scheduled notebook with a different name.

Q-DBX-023 What is medallion architecture? theory easy

Answer

Medallion is bronze, silver, gold. Bronze keeps data as landed. Silver is cleaned and conformed. Gold is business-ready aggregates and dimensions. Each layer has a different contract.

Explanation

Bronze should be replayable and cheap to rebuild from files. Silver applies types, dedup, and keys. Gold is what BI and data products query. Do not skip bronze "to save storage" if you cannot replay. Do not let analysts query raw bronze as the source of truth. Unity Catalog catalogs or schemas often map to layers. Cost: extra copies have a price, but debug and GDPR replay are cheaper when bronze exists.

Trap

Putting business KPIs straight into bronze, or treating gold as an ungoverned dump of every column.

Q-DBX-024 What is Auto Loader? theory medium

Answer

Auto Loader is incremental file ingestion using cloudFiles. It discovers new files, infers schema, and writes with a checkpoint. It is the usual Databricks way to land files into bronze.

Explanation

Directory listing mode lists the prefix. File notification mode uses queue events and scales better on huge landings. Schema lives in a schema location. Bad records can go to rescued data. Combine it with volumes, not random mounts. Exactly-once file processing depends on the checkpoint. Cost: listing every minute on millions of files is expensive, so use notifications at scale. Auto Loader is not a replacement for MERGE into silver.

Trap

Using spark.read in a loop instead of Auto Loader, and hoping you will not reread files.

Q-DBX-025 Why do Structured Streaming checkpoints matter? theory medium

Answer

A checkpoint stores offsets and streaming state. Each stream needs its own durable path. If you lose it, you can reread data or skip data. Never share one checkpoint across two streams.

Explanation

Delta sinks use the checkpoint plus the transaction log for exactly-once table writes. Auto Loader file positions live there too. Put checkpoints on a volume or a dedicated storage prefix with the same governance as the table. Backfill with a new checkpoint, not by deleting prod. If you change the query in incompatible ways, you may need a new checkpoint. Cost is small compared with duplicate gold loads after a lost checkpoint.

Trap

Pointing two jobs at the same checkpoint, or deleting the checkpoint to "fix" a stuck stream.

Q-DBX-026 What are identity columns on Delta tables? theory easy

Answer

An identity column auto-generates unique numeric keys. You declare GENERATED ALWAYS AS IDENTITY or GENERATED BY DEFAULT. Use it for surrogate keys. Do not use it as the only MERGE business key.

Explanation

ALWAYS blocks user inserts into that column. BY DEFAULT lets you supply a value, which is useful in backfills. Identity is per table, not global across a catalog. Clones and CTAS need care because generated values may not copy the way you expect. Production MERGE should still match on customer_id or another business key. Identity is for warehouse-style surrogate keys in gold dimensions.

Trap

Using identity as the CDC match key, then breaking when the same customer is reloaded.

Q-DBX-027 What is Delta Change Data Feed (CDF)? theory medium

Answer

CDF records row-level inserts, updates, and deletes on a Delta table. Downstream jobs read table_changes between versions. You must enable it on the table. VACUUM still limits how far back you can read.

Explanation

Enable delta.enableChangeDataFeed. Readers see change type plus preimage and postimage for updates. This is cleaner than scanning the whole silver table every hour. Gold jobs apply those changes with MERGE. CDF is not the same as cloud object notifications. It is also not a full audit log of who ran the query. Keep enough history for your SLA. Cost: extra change files, less full-table rewrite downstream.

Trap

Thinking CDF is enabled on every Delta table by default.

Q-DBX-028 How does time travel work on Delta tables? theory easy

Answer

Delta keeps old files and a transaction log. You can query VERSION AS OF or TIMESTAMP AS OF. RESTORE can roll the table back. VACUUM and log retention bound how far you can go.

Explanation

DESCRIBE HISTORY shows versions. Time travel is for debug, audit, and fix-forward. It is not an infinite backup. delta.logRetentionDuration and delta.deletedFileRetentionDuration matter. After VACUUM, old versions may fail. Production restores should be rare; prefer writing a new correct batch. Cost: longer retention means more storage. Governance: who can RESTORE production tables should be limited.

Trap

Saying time travel always works for any date in the past.

Q-DBX-029 OPTIMIZE and ZORDER โ€” what do they do? theory medium

Answer

OPTIMIZE compacts small files into larger ones. ZORDER BY also co-locates data by chosen columns so data skipping works better. OPTIMIZE does not delete history. VACUUM does.

Explanation

Small files create thousands of tiny tasks and slow listings. Compaction targets larger files, often around 1 GB, workload dependent. ZORDER is not a B-tree index. It rewrites files so min/max stats are tighter. Pick a few filter columns, not twenty. ZORDER rewrite is heavier than liquid clustering on large tables. Run it on a schedule or let predictive optimization do it for eligible managed tables. Cost is extra write I/O for faster reads.

Trap

Calling ZORDER a database index, or saying OPTIMIZE deletes old files.

Q-DBX-030 What is liquid clustering? theory medium

Answer

Liquid clustering is Delta's current clustering method. You declare CLUSTER BY columns. OPTIMIZE incrementally rewrites files toward that layout. You can change clustering keys without rebuilding partitions.

Explanation

Partition folders are rigid. ZORDER is a full rewrite when you run it. Liquid clustering is a table property plus incremental OPTIMIZE. Writes do not fully recluster by themselves. Predictive optimization can schedule OPTIMIZE on Unity Catalog managed tables. Do not mix old Hive-style partitioning with liquid clustering on the same keys. Choose columns used in filters and joins. This is a production layout choice, not a SQL hint.

Trap

Saying CLUSTER BY reclusters every insert automatically with no OPTIMIZE.

Q-DBX-031 What is predictive optimization? theory medium

Answer

Predictive optimization lets Databricks run maintenance for you. It can OPTIMIZE, VACUUM, and collect stats on eligible Unity Catalog managed tables. It is not a license to ignore file layout on every table.

Explanation

Enable it at account, catalog, schema, or table level depending on the workspace. External tables and some features are not eligible. If it is on, do not also run aggressive hourly OPTIMIZE unless you measured a gap. You still choose clustering keys. You still set retention so VACUUM does not kill time travel too soon. Cost: Databricks spends compute to save later read cost. Watch the billing tables.

Trap

Promising predictive optimization on Hive tables, external tables, or every cloud SKU.

Q-DBX-032 What does VACUUM do, and what is the default retention? theory medium

Answer

VACUUM deletes unreferenced data files older than the retention period. Default retention is 7 days. After those files are gone, time travel to older versions can fail. VACUUM is destructive.

Explanation

OPTIMIZE, MERGE, and DELETE leave old files around so readers and time travel still work. VACUUM reclaims storage. Databricks blocks very short retention unless you disable a safety check. Retention of 0 hours can break concurrent readers. Production should keep at least the time you need to recover a bad load. Cost falls after VACUUM. Recovery risk rises. Pair VACUUM with a tested restore plan.

Trap

Running VACUUM ... RETAIN 0 HOURS in production to "clean everything."

Q-DBX-033 What is MERGE on Delta, and why is it used? theory easy

Answer

MERGE is an atomic upsert. It can update matches, insert new keys, and delete some rows in one table commit. Databricks ETL uses it for CDC and SCD. The source must have at most one row per match key.

Explanation

Separate INSERT plus UPDATE is not one transaction. MERGE is. Deduplicate the source first or MERGE fails. Put the most specific WHEN MATCHED clauses first. Bound the target with a partition or filter when you can, so you do not rewrite the whole lake. MERGE can still rewrite many files, so deletion vectors and clustering matter. Production jobs should log insert/update counts.

Trap

Saying MERGE is just INSERT OVERWRITE of the whole table.

Q-DBX-034 Schema enforcement vs schema evolution theory medium

Answer

Enforcement rejects extra or wrong-typed columns. Evolution allows additive changes when you opt in. Delta is strict by default. Treat schema change as a contract change, not a silent surprise.

Explanation

mergeSchema can add new columns on write. overwriteSchema replaces the schema and can drop columns. Auto Loader has schema evolution modes and rescued data. Gold tables should evolve slower than bronze. Unity Catalog and downstream BI break when names change. Production: add columns in a reviewed change, backfill, then notify consumers. Cost of a surprise string-to-int change is a failed job plus bad dashboards.

Trap

Enabling overwriteSchema on every write "so it never fails."

Q-DBX-035 What is Delta Sharing? theory medium

Answer

Delta Sharing is an open protocol to share live tables without copying them into the consumer's warehouse. The provider grants a share. The recipient reads through Unity Catalog or an open connector. Audit stays with the provider.

Explanation

You share tables, views, or volumes depending on the setup. Recipients get current data, not a weekly dump, unless you design it that way. This beats emailing parquet extracts. You still need least privilege, row filters if required, and a legal agreement. There is network and compute cost on the provider when recipients query. It is not the same as Lakehouse Federation, which pulls from foreign databases.

Trap

Saying Delta Sharing means you zip parquet files and put them on S3.

Q-DBX-036 What is Lakehouse Federation? theory medium

Answer

Federation lets Unity Catalog query external databases in place. You create a connection and a foreign catalog. Queries can push down filters. It is for exploration and light joins, not always for huge ETL.

Explanation

Trap

Using federation to replace bronze ingestion for a multi-terabyte daily fact table.

Q-DBX-037 What is Delta UniForm? theory easy

Answer

UniForm is Delta Universal Format. A Delta table can also write Iceberg metadata. Iceberg clients can then read the same data. Delta remains the writer of truth.

Explanation

Some companies have both Databricks and Iceberg readers. UniForm reduces extra copies. It is not a full Iceberg feature clone, and some reader features lag. Interviews only need this brief idea. Production still needs one owner for writes, or you will corrupt the table. Cost benefit is fewer duplicated lakes.

Trap

Saying UniForm means Databricks no longer uses Delta.

Q-DBX-038 Why do cost tags matter? theory medium

Answer

Tags attach team, product, and environment names to compute and jobs. Finance uses them for chargeback. Cluster policies should force required tags. Untagged clusters become a shared mystery bill.

Explanation

Databricks has custom tags. Cloud resources also have tags. They do not always map 1:1, so design both. Jobs, warehouses, and pipelines all need tags. Unity Catalog has separate classification tags for PII, which is governance, not billing. Look at system billing tables to verify. Production bar: no tag, no cluster. Tags do not reduce usage by themselves; they make owners visible.

Trap

Tagging only the workspace and assuming every job is explained.

Q-DBX-039 What are Databricks Asset Bundles? theory medium

Answer

Asset Bundles are YAML-as-code for jobs, pipelines, and permissions. You deploy the same project to dev and prod. Current docs may say Declarative Automation Bundles. Interviews still say DAB or Asset Bundles.

Explanation

Click-ops jobs drift. Bundles keep cluster size, schedules, and grants in Git. CI impersonates a service principal and deploys. Variables change catalog names per environment. This is how you stop "it worked in my workspace." Bundles do not replace tests. They also do not freeze DBU cost if the YAML asks for huge clusters.

Trap

Keeping production job settings only in the UI because YAML looks extra.

Q-DBX-040 Workspace vs account vs metastore theory medium

Answer

The account is the parent for billing and identity. A workspace is one Databricks deployment that users log into. A Unity Catalog metastore is the governance domain, usually one per region, attached to workspaces.

Explanation

Users and service principals live at the account. Workspaces contain notebooks, jobs, and compute. Two workspaces can share one metastore so prod.sales.orders is the same table. They can also use different metastores, which splits governance. Do not create a metastore per workspace "just in case." Production identity should be account-level groups, not workspace-only users when UC is on.

Trap

Saying a workspace is the same thing as a Unity Catalog metastore.

Q-DBX-041 What are deletion vectors? theory medium

Answer

Deletion vectors mark rows as deleted without rewriting the whole Parquet file. MERGE, UPDATE, and DELETE can become faster. Reads must apply the marks. OPTIMIZE later rewrites clean files.

Explanation

Without them, deleting one row rewrites the entire file. That is write amplification and cost. With them, the write is a small sidecar. Tables on current Databricks often have this on. UniForm and some external readers may have limits, so check if you share files outside Databricks. VACUUM and OPTIMIZE still reclaim space. This is a production write/read trade-off, not a user-facing SQL feature.

Trap

Thinking DELETE always rewrites files, or that deletion vectors shrink storage immediately.

Q-DBX-042 What is the small-file problem on Databricks? theory medium

Answer

Too many tiny files make listing, planning, and task startup slow. Streaming and MERGE create them easily. Fix layout with Auto Loader options, fewer partitions, OPTIMIZE, or liquid clustering.

Explanation

Each file can become a task. Ten thousand 2 MB files are worse than twenty 1 GB files for many scans. Partitioning by a high-cardinality column is a common cause. Auto Loader maxFilesPerTrigger and target file size settings help bronze. Gold needs OPTIMIZE or predictive optimization. Cost shows up as longer jobs and more LIST calls on cloud storage. Do not repartition(1) huge facts as the fix.

Trap

Partitioning a table by user_id and then wondering why there are millions of files.

Q-DBX-043 Auto Loader vs COPY INTO theory medium

Answer

COPY INTO is an idempotent batch load of files into a Delta table. Auto Loader is a streaming cloudFiles source with a checkpoint. Use COPY INTO for simple scheduled batches. Use Auto Loader when files arrive continuously or the prefix is huge.

Explanation

COPY INTO tracks files it already loaded inside the target table. It is easy SQL. It can struggle when the directory has enormous listing costs. Auto Loader can use file notifications and schema evolution. Both should read from volumes. Neither one is MERGE. Bronze first, then MERGE to silver. Cost: running COPY INTO every minute on a giant bucket is the wrong tool.

Trap

Using COPY INTO in a tight loop as if it were a streaming engine.

Q-DBX-044 Shared vs assigned (standard vs dedicated) access mode theory hard

Answer

Assigned or dedicated compute runs as one identity. Standard or shared compute can serve many users with Unity Catalog fine-grained security. Row filters and dynamic views need the shared/standard path. Some RDD and low-level Spark APIs are blocked there.

Explanation

Older names are single-user and shared. Newer names are dedicated and standard. Interactive teams that need table-level GRANT enforcement should use standard access mode. A pipeline that must use RDDs or special libraries may need dedicated compute as the job identity. No-isolation clusters do not fit Unity Catalog. This is a governance question first, then a compatibility question. Pick access mode in the cluster policy so users cannot bypass it.

Trap

Using a no-isolation cluster and saying Unity Catalog still enforces row filters.

Q-DBX-045 DLT / Lakeflow expectations โ€” warn, drop, or fail? theory medium

Answer

Expectations are data quality rules on a pipeline table. A warn rule keeps bad rows and records metrics. A drop rule removes bad rows. A fail rule stops the update. Choose based on whether bad data is worse than downtime.

Explanation

expect logs violations. expect_or_drop filters. expect_or_fail fails the pipeline. Bronze often warns, because you want the raw row. Silver often drops or fails on keys and amounts. Gold usually fails on contract breaks. Metrics should go to alerts. Expectations are not a full test suite and not a unique constraint like a warehouse PK. Production still needs quarantine tables for dropped rows if the business must review them.

Trap

Using only warn rules in gold and then shipping dashboards with silent bad keys.

Q-DBX-046 What is watermarking in Databricks streaming? theory medium

Answer

A watermark tells Spark how late events may arrive. It lets streaming aggregations and stream-stream joins drop old state. Without it, state can grow until the job OOMs.

Explanation

You define watermark on an event-time column, such as 2 hours. Events older than the watermark can be ignored. That is a business choice: late orders after two hours might go to a catch-up batch. Checkpoints store the state. Auto Loader watermarks are not the same thing as event-time watermarks. Production: monitor state size in the streaming UI. Cost of "infinite late data" is memory and shuffle, not just extra rows.

Trap

Watermarking on processing time and thinking you handled late event time.

Q-DBX-047 What is AQE, and do you still need it on Databricks? theory medium

Answer

Adaptive Query Execution changes the plan at runtime. It can coalesce shuffle partitions, switch to a broadcast join, and handle skew. Databricks enables AQE in current runtimes. You still need good keys and clustering.

Explanation

Static spark.sql.shuffle.partitions = 200 is a blunt default. AQE can reduce tiny tasks after a filter. It cannot fix a Python UDF or a huge cartesian join. Photon and AQE work together on SQL plans. Look at the query profile after the run, not only EXPLAIN before the run. Production: leave AQE on unless you have a measured reason. Cost savings come from fewer empty tasks and fewer wrong join strategies.

Trap

Turning AQE off "to make the plan predictable" without measuring skew and shuffle.

Q-DBX-048 When should you use spot / preemptible workers? theory medium

Answer

Spot workers are cheaper VMs that the cloud can take back. Use them for retry-safe job workers. Keep the driver on on-demand. Do not use spot as the only option for a tight SLA job.

Explanation

Databricks can fall back to on-demand if spot is missing. Shuffle loss from a killed worker causes retries. That can erase the savings. Streaming jobs with large state are poor spot candidates. Batch bronze loads with checkpoints are better. Tag the job and watch retry time in the run UI. Cost optimization is real only if the job still finishes inside the SLA.

Trap

Putting the driver on spot to save a bit more money.

Q-DBX-049 Why prefer Unity Catalog volumes over DBFS mounts? theory medium

Answer

Mounts hide cloud keys inside a workspace path. They bypass Unity Catalog grants. Volumes are first-class UC objects with GRANT and audit. New pipelines should read /Volumes/....

Explanation

dbutils.fs.mount was the old pattern. Anyone who can see the mount path can often see the files. Volumes use storage credentials and external locations, or managed storage. The same groups that own the schema own the files. Init scripts, Auto Loader, and ML artifacts can all use volumes. Migration means changing paths and removing mounts. Governance and production both get simpler after that.

Trap

Creating a new mount in 2026 and calling it equivalent to Unity Catalog.

Q-DBX-050 How does Databricks cost work? theory medium

Answer

You pay cloud infrastructure plus Databricks DBUs. Cluster type, serverless, Photon, and runtime change the DBU rate. Idle all-purpose clusters and oversized warehouses are the usual waste. Tags, auto-stop, job clusters, and predictive optimization are the usual controls.

Explanation

A DBU is a Databricks usage unit, not a VM. All-purpose is priced higher than jobs compute. SQL warehouses bill while they are running, so auto-stop matters. Serverless removes VM idle time but not query waste. Storage is a separate cloud bill, reduced by VACUUM and by not copying every layer twice without a reason. Read system.billing.usage if it is enabled. Production FinOps: policy-enforced tags, budgets, and no unowned clusters.

Trap

Optimizing only Spark code and ignoring that an always-on XL warehouse is the real bill.

Q-DBX-051 Create a Unity Catalog Delta table coding easy

Answer

Create the table with USING DELTA in a three-level name. Put comments on the table. Choose managed storage unless you must point at an existing path.

Explanation

Interviewers want the catalog.schema.table name, Delta, and a real schema. Location clauses make the table external. Production tables also need ownership, grants, and clustering or partitioning choices. Do not create tables in the default Hive database.

Code

sql
-- Three-level Unity Catalog name: catalog.schema.table
CREATE TABLE IF NOT EXISTS main.silver.orders (
  order_id STRING NOT NULL,          -- business key
  customer_id STRING NOT NULL,
  amount DECIMAL(12, 2),
  status STRING,
  updated_at TIMESTAMP
)
USING DELTA
COMMENT 'Clean orders used by downstream gold jobs';

What this code does

  1. It creates a managed Delta table in the main catalog and silver schema.
  2. It declares types and NOT NULL on keys.
  3. It stores the table as Delta, so you get ACID, time travel, and MERGE.

Trap

Writing CREATE TABLE orders with no catalog and no USING DELTA.

Q-DBX-052 Create a table with an identity column coding easy

Answer

Use GENERATED ALWAYS AS IDENTITY for a surrogate key. Keep a separate business key for MERGE. Identity is not a replacement for customer_id.

Explanation

Gold dimensions often want a warehouse key plus a source key. ALWAYS blocks users from inserting their own ids. BY DEFAULT is for backfills. Clones and reloads can confuse people who thought identity was globally unique.

Code

sql
CREATE TABLE main.gold.dim_customer (
  -- Surrogate key. Spark fills this in.
  customer_sk BIGINT GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
  customer_id STRING NOT NULL,       -- business key from the source
  email STRING,
  valid_from TIMESTAMP,
  valid_to TIMESTAMP,
  is_current BOOLEAN
)
USING DELTA;

What this code does

  1. It makes customer_sk an auto number.
  2. It still stores customer_id so CDC can match the same person later.
  3. The table is Delta, so SCD2 MERGE can add versions.

Trap

Matching MERGE only on the identity column, then inserting a second version of the same customer.

Q-DBX-053 Add a generated column coding medium

Answer

A generated column is computed from other columns and stored. Use it for a date derived from a timestamp. Queries can filter on the generated column.

Explanation

Generated columns help clustering and skipping when people filter on event_date but writes only have event_time. The expression must be deterministic. Do not use a Python UDF here.

Code

sql
CREATE TABLE main.bronze.events (
  event_id STRING,
  event_time TIMESTAMP,
  -- Stored date derived from event_time
  event_date DATE GENERATED ALWAYS AS (CAST(event_time AS DATE)),
  payload STRING
)
USING DELTA
CLUSTER BY (event_date);

What this code does

  1. It stores event_time as the raw timestamp.
  2. It always computes event_date from that timestamp.
  3. It clusters by event_date so day filters skip files.

Trap

Inserting your own event_date that does not match event_time.

Q-DBX-054 Create a table with liquid clustering coding medium

Answer

Declare CLUSTER BY on the columns used in filters and joins. Then run OPTIMIZE so files actually cluster. Do not Hive-partition those same columns.

Explanation

CLUSTER BY is a layout declaration. Writes do not fully recluster by themselves. Predictive optimization can schedule OPTIMIZE on eligible Unity Catalog managed tables. Changing keys later is ALTER TABLE ... CLUSTER BY.

Code

sql
CREATE TABLE main.silver.orders (
  order_id STRING,
  customer_id STRING,
  order_date DATE,
  amount DECIMAL(12, 2),
  status STRING
)
USING DELTA
-- Liquid clustering keys, not Hive partitions
CLUSTER BY (customer_id, order_date);

-- Incremental rewrite toward the clustering keys
OPTIMIZE main.silver.orders;

What this code does

  1. It creates a Delta table with liquid clustering on customer and date.
  2. OPTIMIZE rewrites files so rows with the same keys sit together.
  3. Later reads that filter on those keys can skip more files.

Trap

Adding PARTITIONED BY (customer_id) and CLUSTER BY (customer_id) on the same table.

Q-DBX-055 MERGE upsert into a Delta table coding medium

Answer

Deduplicate the source, match on the business key, update when the source is newer, and insert when the key is new. One MERGE is one atomic commit.

Explanation

This is the most common Databricks coding question. Clause order matters. Without a newer-row check you can apply stale CDC. Without dedup, MERGE throws. Bound the target if the table is huge.

Code

sql
-- One row per order_id, newest event wins
CREATE OR REPLACE TEMP VIEW orders_src AS
SELECT order_id, status, amount, updated_at
FROM (
  SELECT
    order_id,
    status,
    amount,
    updated_at,
    ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY updated_at DESC) AS rn
  FROM main.bronze.orders_landing
)
WHERE rn = 1;

MERGE INTO main.silver.orders AS t
USING orders_src AS s
ON t.order_id = s.order_id
WHEN MATCHED AND s.updated_at > t.updated_at THEN
  UPDATE SET
    t.status = s.status,
    t.amount = s.amount,
    t.updated_at = s.updated_at
WHEN NOT MATCHED THEN
  INSERT (order_id, status, amount, updated_at)
  VALUES (s.order_id, s.status, s.amount, s.updated_at);

What this code does

  1. It ranks landing rows so each order_id appears once.
  2. It updates silver only when the source timestamp is newer.
  3. It inserts keys that silver does not have yet.
  4. Both actions commit together.

Trap

Skipping dedup, then hitting "multiple source rows matched" and rewriting the MERGE instead of the source.

Q-DBX-056 MERGE that updates, inserts, and deletes coding medium

Answer

Put the delete rule in a matched clause before the generic update. Insert unmatched source rows. Do not use NOT MATCHED BY SOURCE unless you really want to delete target rows missing from a partial batch.

Explanation

CDC often sends DELETE as an operation flag. A full snapshot can use WHEN NOT MATCHED BY SOURCE. A partial incremental batch must not. Production MERGE should log operation counts.

Code

sql
MERGE INTO main.silver.orders AS t
USING main.bronze.orders_cdc AS s
ON t.order_id = s.order_id
-- Handle deletes first so they are not updated instead
WHEN MATCHED AND s.op_type = 'DELETE' THEN DELETE
WHEN MATCHED AND s.updated_at > t.updated_at THEN
  UPDATE SET *
WHEN NOT MATCHED AND s.op_type <> 'DELETE' THEN
  INSERT *;

What this code does

  1. It matches on order_id.
  2. It deletes silver rows whose CDC event is a delete.
  3. It updates remaining matches when the event is newer.
  4. It inserts new keys unless the event itself is a delete.

Trap

Using WHEN NOT MATCHED BY SOURCE THEN DELETE on an incremental CDC file, which wipes most of the table.

Q-DBX-057 Deduplicate source rows before MERGE in PySpark coding hard

Answer

Window by the business key, keep the newest row, then merge. Spark MERGE cannot pick a winner if two source rows match one target row.

Explanation

This is the coding follow-up after a MERGE error. dropDuplicates(["order_id"]) is not enough because it is not ordered. Use updated_at or an event sequence. Then call DeltaTable.merge.

Code

python โ€” editable
from pyspark.sql import Window
from pyspark.sql.functions import col, row_number
from delta.tables import DeltaTable

# Newest event per order_id
w = Window.partitionBy("order_id").orderBy(col("updated_at").desc())
src = (
    spark.table("main.bronze.orders_landing")
    .withColumn("rn", row_number().over(w))
    .filter(col("rn") == 1)
    .drop("rn")
)

# Atomic upsert into silver
(
    DeltaTable.forName(spark, "main.silver.orders")
    .alias("t")
    .merge(src.alias("s"), "t.order_id = s.order_id")
    .whenMatchedUpdate(
        condition="s.updated_at > t.updated_at",
        set={
            "status": "s.status",
            "amount": "s.amount",
            "updated_at": "s.updated_at",
        },
    )
    .whenNotMatchedInsertAll()
    .execute()
)

What this code does

  1. It ranks landing rows inside each order_id.
  2. It keeps rank 1, the newest event.
  3. It updates silver when the event is newer.
  4. It inserts brand-new orders.

Trap

Calling dropDuplicates(["order_id"]) and assuming Spark kept the latest amount.

Q-DBX-058 Compact files with OPTIMIZE coding easy

Answer

Run OPTIMIZE on the table. It rewrites small files into larger files. It does not delete old files. VACUUM does that later.

Explanation

Streaming bronze and MERGE create small files. OPTIMIZE reduces task count on the next read. On liquid clustered tables, OPTIMIZE also moves data toward clustering keys. Predictive optimization may already do this on managed tables.

Code

sql
-- Compact current files in the whole table
OPTIMIZE main.silver.orders;

-- Optional: compact only yesterday if the table is still partitioned
-- OPTIMIZE main.silver.orders WHERE order_date = current_date() - 1;

What this code does

  1. It reads the current small files.
  2. It writes fewer larger files.
  3. The table version moves forward. Old files remain until VACUUM.

Trap

Thinking OPTIMIZE frees storage the same minute it runs.

Q-DBX-059 OPTIMIZE with ZORDER coding medium

Answer

Use OPTIMIZE ... ZORDER BY when the table is not on liquid clustering. Pick a few columns that appear in filters. ZORDER is a rewrite, not a B-tree.

Explanation

ZORDER co-locates column values so min/max stats skip more files. Do not ZORDER 10 columns. Do not ZORDER the partition column only. Prefer liquid clustering on new Unity Catalog tables.

Code

sql
-- Rewrite files so customer_id and order_date values sit together
OPTIMIZE main.silver.orders
ZORDER BY (customer_id, order_date);

What this code does

  1. It compacts files.
  2. It sorts data along customer_id and order_date.
  3. Later filters on those columns can skip more files.

Trap

ZORDERing every column "just in case," which costs write time and helps almost nothing.

Q-DBX-060 VACUUM a Delta table coding medium

Answer

VACUUM removes unreferenced files older than retention. Default is 7 days. Dry-run first in production. Short retention breaks time travel.

Explanation

After OPTIMIZE and MERGE, old parquet files linger on purpose. VACUUM is the storage reclaim step. Databricks blocks retention under 7 days unless you disable a safety check. Do not disable that in prod without a reason.

Code

sql
-- See which files would be deleted
VACUUM main.silver.orders DRY RUN;

-- Delete unreferenced files older than 7 days
VACUUM main.silver.orders RETAIN 168 HOURS;

What this code does

  1. Dry run lists candidate files and deletes nothing.
  2. The real VACUUM deletes files older than 168 hours that the current table no longer needs.
  3. Time travel older than that retention can fail.

Trap

Running RETAIN 0 HOURS because the table "looks messy."

Q-DBX-061 Override VACUUM safety retention coding hard

Answer

You can disable the retention check and vacuum sooner. Concurrent readers and time travel can break. This is an emergency tool, not a daily job.

Explanation

Interviewers ask this to hear the risk, not the syntax. Streaming jobs, long BI queries, and VERSION AS OF need old files. Cost savings are real. Data loss is also real. Prefer 7 days plus predictive optimization.

Code

sql
-- Turns off the 7-day guard. Dangerous in production.
SET spark.databricks.delta.retentionDurationCheck.enabled = false;

-- Deletes unreferenced files immediately
VACUUM main.sandbox.scratch_orders RETAIN 0 HOURS;

-- Put the guard back
SET spark.databricks.delta.retentionDurationCheck.enabled = true;

What this code does

  1. It disables the safety check that blocks short retention.
  2. It deletes unreferenced files with zero hours of grace.
  3. It turns the check back on so later jobs are protected.

Trap

Leaving the safety check off at the workspace level after one sandbox test.

Q-DBX-062 Query a table with time travel coding easy

Answer

Use VERSION AS OF or TIMESTAMP AS OF. Check DESCRIBE HISTORY first. Time travel only works while files still exist.

Explanation

This is how you debug a bad MERGE. Compare current gold to last night. Do not build nightly BI on time travel if VACUUM is 7 days. For a lasting fix, RESTORE or write a new table.

Code

sql
-- What versions exist?
DESCRIBE HISTORY main.silver.orders;

-- Read one old version
SELECT order_id, status, amount
FROM main.silver.orders VERSION AS OF 12;

-- Read the table as of a timestamp
SELECT count(*) AS rows_last_night
FROM main.silver.orders TIMESTAMP AS OF '2026-09-15T22:00:00';

What this code does

  1. History shows version numbers and timestamps.
  2. The first SELECT reads version 12 without changing the table.
  3. The second SELECT reads the snapshot at that time.

Trap

Using time travel after VACUUM and assuming the version must still be there.

Q-DBX-063 Read table history coding easy

Answer

DESCRIBE HISTORY lists commits, operation type, user, and version. Use it before restore, CDF, or time travel. It is metadata, not a row-level audit of SELECT.

Explanation

You will see MERGE, WRITE, OPTIMIZE, RESTORE. The operationMetrics column has files added and removed. Governance of who ran the write is here. Who queried the table is in audit logs, not this command.

Code

sql
-- Latest 20 commits on the table
DESCRIBE HISTORY main.silver.orders LIMIT 20;

-- Only MERGE commits
SELECT version, timestamp, userName, operation, operationMetrics
FROM (DESCRIBE HISTORY main.silver.orders)
WHERE operation = 'MERGE';

What this code does

  1. It prints recent commits.
  2. It filters to MERGE so you can find the bad load.
  3. Metrics tell you how many rows and files that MERGE touched.

Trap

Treating DESCRIBE HISTORY as a list of every SELECT against the table.

Q-DBX-064 Restore a table to an older version coding medium

Answer

RESTORE TABLE moves the current pointer back to an old snapshot. It is a new commit, not a silent rewrite of history. Need MODIFY privilege. VACUUM can make the target version unrestorable.

Explanation

Restore is for a bad overwrite. After restore, tell downstream jobs. A safer pattern is clone the old version to a side table, validate, then swap. Production should restrict who can restore gold.

Code

sql
-- Confirm the version you want
DESCRIBE HISTORY main.silver.orders LIMIT 10;

-- Roll the table back. This creates a new version.
RESTORE TABLE main.silver.orders TO VERSION AS OF 12;

-- Alternative: restore by time
-- RESTORE TABLE main.silver.orders TO TIMESTAMP AS OF '2026-09-15T22:00:00';

What this code does

  1. It checks history so you pick the right version.
  2. It restores data files from version 12 as the current table.
  3. History still shows the bad commit and the restore commit.

Trap

Restoring prod from a notebook as a user, with no clone and no validation query.

Q-DBX-065 Enable CDF and read table_changes coding medium

Answer

Set delta.enableChangeDataFeed = true. Read table_changes('table', from, to). Apply those rows to gold with MERGE. History must still exist.

Explanation

CDF is cheaper than scanning all of silver. You get insert, update, delete, plus preimage and postimage. Enable it before you need the first incremental window. Starting CDF in the middle does not invent old changes.

Code

sql
-- Turn on row change tracking
ALTER TABLE main.silver.orders
SET TBLPROPERTIES (delta.enableChangeDataFeed = true);

-- Changes from version 10 through 15
SELECT
  order_id,
  status,
  amount,
  _change_type,     -- insert, update_preimage, update_postimage, delete
  _commit_version
FROM table_changes('main.silver.orders', 10, 15);

What this code does

  1. It enables CDF on future commits.
  2. It reads the change rows between two versions.
  3. _change_type tells you how to MERGE into gold.

Trap

Querying table_changes without enabling the table property, or using a version VACUUM already removed.

Q-DBX-066 Load files with COPY INTO coding medium

Answer

COPY INTO loads new files from a volume into a Delta table. It remembers files it already loaded. Use it for scheduled batch bronze, not for CDC MERGE.

Explanation

Idempotent re-runs are the point. force reloads everything and can duplicate rows. mergeSchema adds columns. For continuous arrival or huge prefixes, Auto Loader is better.

Code

sql
-- Idempotent batch load: files already copied are skipped
COPY INTO main.bronze.orders
FROM '/Volumes/main/raw/landing/orders'   -- Unity Catalog volume, not a mount
FILEFORMAT = JSON
FORMAT_OPTIONS ('inferSchema' = 'true', 'primitivesAsString' = 'false')
COPY_OPTIONS ('mergeSchema' = 'true');    -- add new columns; do not force reload

What this code does

  1. It reads JSON files from a governed volume.
  2. It inserts only files not already tracked by this table.
  3. It allows new columns to be added on the bronze table.

Trap

Setting 'force' = 'true' every run and creating duplicate bronze rows.

Q-DBX-067 Auto Loader sketch to a Delta table coding medium

Answer

Read cloudFiles, set a schema location, write Delta with a unique checkpoint. Use a volume for landing, schema, and checkpoint. Trigger availableNow for batch-style microbatches.

Explanation

This is the standard bronze pattern. Schema location is not the same as checkpoint. File notification mode is for large prefixes. Rescued data holds extra columns that do not match the schema.

Code

python โ€” editable
(
    spark.readStream.format("cloudFiles")
    .option("cloudFiles.format", "json")
    # Where Auto Loader stores inferred schema
    .option("cloudFiles.schemaLocation", "/Volumes/main/ops/schema/orders")
    .option("cloudFiles.inferColumnTypes", "true")
    .option("cloudFiles.schemaEvolutionMode", "addNewColumns")
    .load("/Volumes/main/raw/landing/orders")
    .writeStream.format("delta")
    # Unique checkpoint for this stream only
    .option("checkpointLocation", "/Volumes/main/ops/checkpoints/orders_bronze")
    .option("mergeSchema", "true")
    .trigger(availableNow=True)
    .toTable("main.bronze.orders")
)

What this code does

  1. It incrementally discovers new JSON files.
  2. It infers and evolves schema from a durable schema location.
  3. It writes new rows to a Delta table with exactly-once checkpoints.
  4. availableNow processes pending files and stops.

Trap

Sharing one checkpoint path across two Auto Loader jobs.

Q-DBX-068 DLT / Lakeflow expectations sketch coding medium

Answer

Declare a table with @dlt.table and attach expectations. Warn, drop, or fail. Interviews still say DLT. Current product name is Lakeflow Spark Declarative Pipelines.

Explanation

Bronze usually keeps rows. Silver drops or fails on keys. Expectations are metrics plus optional filters, not a unique index. The pipeline still needs a service principal, alerts, and a catalog target.

Code

python โ€” editable
import dlt
from pyspark.sql.functions import col

@dlt.table(comment="Raw orders as landed")
def bronze_orders():
    return spark.readStream.format("cloudFiles") \
        .option("cloudFiles.format", "json") \
        .load("/Volumes/main/raw/landing/orders")

@dlt.table(comment="Typed orders")
@dlt.expect_or_drop("valid_order_id", "order_id IS NOT NULL")
@dlt.expect("positive_amount", "amount > 0")
def silver_orders():
    # Read the upstream DLT table, not a random Spark table name
    return dlt.read_stream("bronze_orders").select(
        col("order_id").cast("string").alias("order_id"),
        col("amount").cast("decimal(12,2)").alias("amount"),
        col("status").cast("string").alias("status"),
    )

What this code does

  1. It lands files into a bronze streaming table.
  2. It builds silver from bronze.
  3. Null order_id rows are dropped.
  4. Negative amounts are counted but kept.

Trap

Using spark.table("bronze_orders") inside DLT instead of dlt.read / dlt.read_stream.

Q-DBX-069 GRANT on catalog, schema, and table coding easy

Answer

Grant USE CATALOG, then USE SCHEMA, then object rights. Grant to a group or service principal. SELECT alone on the table is not enough.

Explanation

This is the Unity Catalog coding question. Least privilege: analysts get SELECT on gold, engineers get MODIFY on silver. Show grants after you change them. Production should not grant ALL PRIVILEGES on the catalog to a person.

Code

sql
-- Enter the catalog
GRANT USE CATALOG ON CATALOG main TO `data_analysts`;

-- Enter the schema
GRANT USE SCHEMA ON SCHEMA main.gold TO `data_analysts`;

-- Read the table
GRANT SELECT ON TABLE main.gold.daily_revenue TO `data_analysts`;

-- Job identity needs write on silver
GRANT USE SCHEMA ON SCHEMA main.silver TO `jobs-orders-sp`;
GRANT SELECT, MODIFY ON TABLE main.silver.orders TO `jobs-orders-sp`;

What this code does

  1. It lets analysts open the main catalog.
  2. It lets them open the gold schema.
  3. It lets them read one gold table.
  4. It lets the job service principal update silver orders.

Trap

Granting SELECT on the table only, then wondering why the user sees "schema not found."

Q-DBX-070 Set Spark conf on Databricks coding easy

Answer

Use SET in SQL or spark.conf.set in Python. Session conf is not a workspace default. Cluster policies should pin production conf.

Explanation

Shuffle partitions, AQE, and Delta safety checks are common. Serverless may ignore some classic cluster knobs. Do not hide secrets in Spark conf. Put durable settings in the job cluster YAML.

Code

python โ€” editable
# Let AQE pick shuffle partitions
spark.conf.set("spark.sql.adaptive.enabled", "true")
spark.conf.set("spark.sql.shuffle.partitions", "auto")

# Keep the VACUUM 7-day guard on
spark.conf.set("spark.databricks.delta.retentionDurationCheck.enabled", "true")

# Confirm
print(spark.conf.get("spark.sql.adaptive.enabled"))

What this code does

  1. It turns AQE on for this session.
  2. It avoids a hard-coded 200 shuffle partitions.
  3. It keeps short VACUUM blocked.
  4. It prints the AQE flag so you can show the interviewer.

Trap

Setting conf in a notebook and assuming every job cluster picked it up.

Q-DBX-071 Deep clone and shallow clone coding medium

Answer

Deep clone copies data and metadata. Shallow clone copies metadata and reuses data files. Shallow is fast and cheap. VACUUM on the source can break a shallow clone.

Explanation

Use deep clone for a durable sandbox or migration. Use shallow clone for a quick dev table. Clones are new tables with their own history from that point. Grants do not automatically copy the same way people expect, so grant again.

Code

sql
-- Full copy of files. Safe if source is vacuumed later.
CREATE TABLE main.sandbox.orders_deep
DEEP CLONE main.silver.orders;

-- Metadata-only clone. Fast. Shares data files with source.
CREATE TABLE main.sandbox.orders_shallow
SHALLOW CLONE main.silver.orders;

-- Clone a past version
CREATE TABLE main.sandbox.orders_v12
DEEP CLONE main.silver.orders VERSION AS OF 12;

What this code does

  1. Deep clone writes a standalone copy.
  2. Shallow clone creates a new table name that points at existing files.
  3. Version clone freezes last night's snapshot into a new table.

Trap

Shallow cloning prod, vacuuming prod the next day, then using the clone as a backup.

Q-DBX-072 Convert Parquet to Delta coding medium

Answer

CONVERT TO DELTA adds a _delta_log next to existing parquet. It does not rewrite every file. If the parquet is partitioned, pass PARTITIONED BY.

Explanation

This is for legacy lakes. After convert, you can MERGE and time travel. Concurrent writers during convert are unsafe. New tables should be created as Delta, not converted later.

Code

sql
-- In-place convert of a parquet prefix
CONVERT TO DELTA parquet.`/Volumes/main/raw/legacy/orders`
PARTITIONED BY (order_date DATE);

-- Register it as a UC table on that path
CREATE TABLE main.bronze.legacy_orders
USING DELTA
LOCATION '/Volumes/main/raw/legacy/orders';

What this code does

  1. It writes Delta transaction log files beside the parquet files.
  2. It records order_date partitions in that log.
  3. It exposes the converted path as a Unity Catalog table.

Trap

Running convert while another job still writes parquet into the same folder.

Q-DBX-073 Create a volume and read files coding medium

Answer

Create a volume in a schema. Put files under /Volumes/catalog/schema/volume. Grant READ VOLUME to the job identity. Read with COPY INTO or Auto Loader, not a DBFS mount.

Explanation

Volumes govern files the same way tables govern rows. Managed volumes use Unity Catalog storage. External volumes use an external location. This is the current landing-zone pattern. Analysts should not need the cloud key.

Code

sql
-- File folder inside the schema, not a table
CREATE VOLUME IF NOT EXISTS main.raw.landing
COMMENT 'Source files for bronze ingestion';

-- Job identity can list and read files
GRANT READ VOLUME ON VOLUME main.raw.landing TO `jobs-orders-sp`;

-- Load only new files from that volume into bronze
COPY INTO main.bronze.orders
FROM '/Volumes/main/raw/landing/orders'
FILEFORMAT = JSON;

What this code does

  1. It creates a governed volume named landing.
  2. It grants the job service principal file read access.
  3. It copies new JSON files from /Volumes/... into a Delta bronze table.

Trap

Creating a DBFS mount with a storage key and calling that a volume.

Q-DBX-074 Append with schema evolution coding medium

Answer

Use mergeSchema on an append write so new columns can be added. Do not use this as a license to change types at random. Gold should evolve slower than bronze.

Explanation

Delta rejects unknown columns by default. Auto Loader and bronze often need additive evolution. Downstream MERGE must then select the new columns or ignore them. Production: add the column in a review, then turn this on for one job, then notify consumers.

Code

python โ€” editable
# Landing files now include coupon_code
df = spark.read.json("/Volumes/main/raw/landing/orders")

(
    df.write.format("delta")
    .mode("append")
    # Add new columns; do not overwrite the table
    .option("mergeSchema", "true")
    .saveAsTable("main.bronze.orders")
)

What this code does

  1. It reads the latest JSON files.
  2. It appends rows to bronze.
  3. If coupon_code is new, Delta adds that column instead of failing.

Trap

Using overwriteSchema on an append because you mixed it up with mergeSchema.

Q-DBX-075 Overwrite a table schema coding medium

Answer

overwriteSchema replaces the table schema during overwrite. It can drop columns. Use it only on sandbox tables or a planned rebuild. It is not a daily ETL switch.

Explanation

This is how people accidentally delete a gold column and break BI. Prefer additive mergeSchema, or CTAS into a new table and swap. Unity Catalog permissions stay on the table name, but downstream queries still break.

Code

python โ€” editable
# Rebuild a sandbox table with a new schema
df = spark.read.table("main.bronze.orders").select(
    "order_id",
    "amount",
    "status",
)

(
    df.write.format("delta")
    .mode("overwrite")
    # Replaces columns. Drops anything not in df.
    .option("overwriteSchema", "true")
    .saveAsTable("main.sandbox.orders_rebuild")
)

What this code does

  1. It selects a smaller set of columns.
  2. It overwrites a sandbox table.
  3. The table schema becomes exactly those three columns.

Trap

Running overwriteSchema on main.gold.daily_revenue to "make the job stop failing."

Q-DBX-076 SCD Type 2 MERGE coding hard

Answer

Close the current dimension row when attributes change. Insert a new current row for that business key. Keep history with valid_from, valid_to, and is_current. Match on the business key plus is_current = true.

Explanation

This is the classic warehouse coding question on Databricks. Hash tracked columns so you do not close a row when nothing changed. Identity columns can fill the surrogate key. Do not overwrite the old address. Audit reports need the old row.

Code

sql
-- Stage hashed attributes
CREATE OR REPLACE TEMP VIEW customer_src AS
SELECT
  customer_id,
  email,
  address,
  sha2(concat_ws('||', email, address), 256) AS hash_value
FROM main.silver.customer;

-- Close changed current rows, insert brand-new customers
MERGE INTO main.gold.dim_customer AS t
USING customer_src AS s
ON t.customer_id = s.customer_id AND t.is_current = true
WHEN MATCHED AND t.hash_value <> s.hash_value THEN
  UPDATE SET
    t.is_current = false,
    t.valid_to = current_timestamp()
WHEN NOT MATCHED THEN
  INSERT (customer_id, email, address, hash_value, valid_from, valid_to, is_current)
  VALUES (
    s.customer_id, s.email, s.address, s.hash_value,
    current_timestamp(), TIMESTAMP '9999-12-31', true
  );

-- Insert a new current version for customers MERGE just closed
INSERT INTO main.gold.dim_customer (
  customer_id, email, address, hash_value, valid_from, valid_to, is_current
)
SELECT
  s.customer_id, s.email, s.address, s.hash_value,
  current_timestamp(), TIMESTAMP '9999-12-31', true
FROM customer_src s
WHERE NOT EXISTS (
  -- New and unchanged customers already have a current row
  SELECT 1
  FROM main.gold.dim_customer c
  WHERE c.customer_id = s.customer_id
    AND c.is_current = true
);

What this code does

  1. It hashes email and address so unchanged customers are skipped.
  2. MERGE closes the current row when the hash changes.
  3. MERGE inserts customers who never existed.
  4. INSERT adds a new current version for the customers who just closed.

Trap

Updating email in place on the current row and calling it Type 2 because the table has a valid_from column.

Q-DBX-077 Write a Delta stream with a checkpoint coding medium

Answer

Use writeStream to a Delta table and set checkpointLocation to a unique volume path. Do not share that path. Losing it can reread files or skip data.

Explanation

The checkpoint stores offsets. The Delta log stores table commits. Together they give exactly-once table writes. availableNow is the usual batch-style trigger on Databricks. Production jobs should fail if the checkpoint path is missing, not create a second one silently.

Code

python โ€” editable
(
    spark.readStream.table("main.bronze.orders")
    .writeStream.format("delta")
    # One checkpoint per stream. Durable volume path.
    .option("checkpointLocation", "/Volumes/main/ops/checkpoints/orders_silver_stream")
    .outputMode("append")
    .trigger(availableNow=True)
    .toTable("main.silver.orders_stream")
)

What this code does

  1. It reads new bronze rows as a stream.
  2. It writes them into a silver Delta table.
  3. It stores offsets under a volume checkpoint so reruns do not duplicate.
  4. availableNow processes pending data and stops.

Trap

Leaving checkpointLocation off, or pointing two jobs at the same checkpoint.

Q-DBX-078 Read a secret in a notebook or job coding easy

Answer

Call dbutils.secrets.get. Put the value in a local variable. Never print it. Scope ACLs decide who can read the key.

Explanation

Jobs should use a service principal that can read the scope. Source code should contain the scope name and key name only. Cloud-backed scopes are easier to rotate. If you print or display the secret, it can land in logs.

Code

python โ€” editable
# Scope name and key name are not the secret
jdbc_url = dbutils.secrets.get(scope="prod-kv", key="orders-jdbc-url")
jdbc_pass = dbutils.secrets.get(scope="prod-kv", key="orders-jdbc-password")

# Use the secret at runtime. Do not print it.
df = (
    spark.read.format("jdbc")
    .option("url", jdbc_url)
    .option("dbtable", "public.orders")
    .option("user", "orders_etl")
    .option("password", jdbc_pass)
    .load()
)
df.write.format("delta").mode("overwrite").saveAsTable("main.bronze.orders_jdbc")

What this code does

  1. It fetches the JDBC URL and password from a secret scope.
  2. It reads the source database without putting the password in Git.
  3. It writes the result to a bronze Delta table.

Trap

Pasting the password into a widget, a cell comment, or print(jdbc_pass).

Q-DBX-079 Apply a Unity Catalog column mask coding hard

Answer

Create a SQL function that returns the masked value. Attach it to the column. Every engine that uses Unity Catalog then sees the mask. Test as a non-admin user.

Explanation

Masks are governance, not a view people can skip. Account groups drive the CASE. Shared or standard access mode is required for this to apply on a cluster. There is some query cost. Do not mask in Python and leave SQL open.

Code

sql
-- Who sees full email vs a masked email
CREATE OR REPLACE FUNCTION main.gold.mask_email(email STRING)
RETURN CASE
  WHEN is_account_group_member('pii_admins') THEN email
  ELSE concat(left(email, 2), '***@***')
END;

ALTER TABLE main.gold.dim_customer
ALTER COLUMN email SET MASK main.gold.mask_email;

-- Analyst query. pii_admins see the real email. Others see the mask.
SELECT customer_id, email
FROM main.gold.dim_customer
LIMIT 20;

What this code does

  1. It creates a masking function in the gold schema.
  2. It attaches that function to email.
  3. Later SELECTs rewrite the column based on group membership.

Trap

Building a "safe view" and leaving SELECT on the base table for the same users.

Q-DBX-080 Change liquid clustering keys coding medium

Answer

ALTER TABLE ... CLUSTER BY changes the clustering keys. Run OPTIMIZE so files move. Old files stay until OPTIMIZE and VACUUM. You do not need to rebuild partitions.

Explanation

This is why liquid clustering beat Hive partitions for many tables. Predictive optimization can OPTIMIZE eligible managed tables for you. Choose keys from real filters. Do not change keys every week.

Code

sql
-- New clustering keys used by current queries
ALTER TABLE main.silver.orders
CLUSTER BY (customer_id, order_date);

-- Incremental rewrite toward the new keys
OPTIMIZE main.silver.orders;

What this code does

  1. It declares new liquid clustering columns.
  2. OPTIMIZE rewrites data files toward those columns.
  3. Reads that filter on customer and date can skip more files after the rewrite.

Trap

Dropping and recreating the table just to change clustering keys.

Q-DBX-081 Create a gold table with CTAS coding easy

Answer

CREATE TABLE ... USING DELTA AS SELECT builds a table from a query. Add clustering if the gold table is large. This replaces a notebook full of saveAsTable plus a later ALTER.

Explanation

CTAS is good for gold rebuilds. CREATE OR REPLACE TABLE is destructive for readers during the swap, so use it with a schedule and a contract. Managed UC tables keep the data lifecycle in the catalog.

Code

sql
-- Rebuild gold from silver. This replaces the table snapshot.
CREATE OR REPLACE TABLE main.gold.daily_revenue
USING DELTA
CLUSTER BY (order_date)   -- dashboard filters are almost always by date
COMMENT 'One row per day of completed order amount'
AS
SELECT
  order_date,
  sum(amount) AS revenue,
  count(*) AS order_cnt
FROM main.silver.orders
WHERE status = 'COMPLETED'   -- do not count cancelled orders as revenue
GROUP BY order_date;

What this code does

  1. It aggregates completed silver orders by date.
  2. It writes a managed Delta gold table.
  3. It clusters by order_date for dashboard filters.

Trap

CTAS into gold with no WHERE on status, then calling it revenue.

Q-DBX-082 Replace one partition or date slice coding medium

Answer

INSERT ... REPLACE WHERE overwrites only the rows that match the predicate. Use it to reload one day. The predicate in REPLACE WHERE must match the data you insert, or you can drop extra rows.

Explanation

This is safer than overwriting the whole silver table. It is still a data rewrite for that slice. Combine with a volume landing of the fixed files. Time travel can undo a bad replace if you have not vacuumed.

Code

sql
-- Rebuild only 2026-09-15. Other dates stay as they are.
INSERT INTO main.silver.orders
REPLACE WHERE order_date = DATE '2026-09-15'
SELECT
  order_id,
  customer_id,
  order_date,
  amount,
  status,
  updated_at
FROM main.bronze.orders_reprocessed
WHERE order_date = DATE '2026-09-15';

What this code does

  1. It deletes the current 2026-09-15 slice from silver as part of the write.
  2. It inserts the reprocessed rows for that date.
  3. Other dates are not rewritten.

Trap

Using REPLACE WHERE order_date >= '2026-09-01' while the SELECT only has one day, which drops the rest of the month.

Q-DBX-083 foreachBatch MERGE from a stream coding hard

Answer

Read a stream, then foreachBatch runs a MERGE on each microbatch. Deduplicate inside the batch. Give the stream its own checkpoint. This is how streaming CDC becomes silver.

Explanation

Append mode cannot update existing keys. MERGE can. foreachBatch is not exactly-once unless the MERGE is idempotent and the checkpoint is safe. Use availableNow for scheduled catch-up. Watch small files.

Code

python โ€” editable
from pyspark.sql import Window
from pyspark.sql.functions import col, row_number
from delta.tables import DeltaTable

def merge_batch(batch_df, batch_id):
    # One source row per key inside this microbatch
    w = Window.partitionBy("order_id").orderBy(col("updated_at").desc())
    src = (
        batch_df.withColumn("rn", row_number().over(w))
        .filter(col("rn") == 1)
        .drop("rn")
    )
    (
        DeltaTable.forName(spark, "main.silver.orders")
        .alias("t")
        .merge(src.alias("s"), "t.order_id = s.order_id")
        .whenMatchedUpdate(
            condition="s.updated_at > t.updated_at",
            set={
                "status": "s.status",
                "amount": "s.amount",
                "updated_at": "s.updated_at",
            },
        )
        .whenNotMatchedInsertAll()
        .execute()
    )

(
    spark.readStream.table("main.bronze.orders")
    .writeStream.foreachBatch(merge_batch)
    .option("checkpointLocation", "/Volumes/main/ops/checkpoints/orders_merge")
    .outputMode("update")
    .trigger(availableNow=True)
    .start()
)

What this code does

  1. Each microbatch is a DataFrame of new bronze rows.
  2. The function keeps the newest event per order_id.
  3. MERGE updates silver or inserts new keys.
  4. The checkpoint records which bronze data was already applied.

Trap

Merging the raw batch with duplicate keys, or using append mode and hoping updates appear.

Q-DBX-084 Apply CDF changes to a gold table coding hard

Answer

Read table_changes from the last processed version. Keep update postimages and inserts. Merge them into gold. Store the new version so the next run is incremental.

Explanation

This is cheaper than rebuilding gold every hour. You must enable CDF on silver first. Skip update_preimage if you only need the latest row. Deletes need a delete clause. If VACUUM removed the versions, you must full-refresh.

Code

python โ€” editable
from pyspark.sql.functions import col
from delta.tables import DeltaTable

source = "main.silver.orders"
from_version = 10  # last version already applied
to_version = spark.sql(f"DESCRIBE HISTORY {source} LIMIT 1").collect()[0]["version"]

changes = (
    spark.read.format("delta")
    .option("readChangeFeed", "true")
    .option("startingVersion", from_version + 1)
    .option("endingVersion", to_version)
    .table(source)
    .filter(col("_change_type").isin("insert", "update_postimage", "delete"))
    # Keep _change_type for MERGE rules. Drop other CDF metadata.
    .drop("_commit_version", "_commit_timestamp")
)

(
    DeltaTable.forName(spark, "main.gold.orders_current")
    .alias("t")
    .merge(changes.alias("s"), "t.order_id = s.order_id")
    .whenMatchedDelete(condition="s._change_type = 'delete'")
    .whenMatchedUpdate(
        condition="s._change_type = 'update_postimage'",
        set={
            "status": "s.status",
            "amount": "s.amount",
            "updated_at": "s.updated_at",
        },
    )
    .whenNotMatchedInsert(
        condition="s._change_type <> 'delete'",
        values={
            "order_id": "s.order_id",
            "status": "s.status",
            "amount": "s.amount",
            "updated_at": "s.updated_at",
        },
    )
    .execute()
)

What this code does

  1. It finds the latest silver version.
  2. It reads CDF rows after the last applied version.
  3. It keeps inserts, update postimages, and deletes.
  4. It MERGEs those changes into a gold current table.

Trap

Applying both update_preimage and update_postimage, which can undo the update.

Q-DBX-085 Collect table statistics coding easy

Answer

ANALYZE TABLE ... COMPUTE STATISTICS gathers stats for the optimizer. Predictive optimization can do this on eligible managed tables. Stale stats cause bad join plans.

Explanation

Photon and AQE still benefit from file stats and table stats. Data skipping uses min/max in the Delta log. ANALYZE helps cost-based choices such as broadcast. Run it after a large load if predictive optimization is off.

Code

sql
-- Table-level and column-level stats for the optimizer
ANALYZE TABLE main.silver.orders COMPUTE STATISTICS FOR ALL COLUMNS;

-- Confirm size and details
DESCRIBE DETAIL main.silver.orders;

What this code does

  1. It computes stats on every column.
  2. The optimizer can pick better joins later.
  3. DESCRIBE DETAIL shows size, files, and location.

Trap

Never analyzing a 2 TB table, then forcing a broadcast join because EXPLAIN "looked small."

Q-DBX-086 Add NOT NULL and CHECK constraints coding medium

Answer

Set NOT NULL on keys. Add CHECK for simple rules such as amount > 0. Writes that break the rule fail. Constraints are not a full SCD or uniqueness system.

Explanation

Delta does not give you a warehouse primary key with indexes. Constraints still catch bad ETL early. Put heavy quality rules in DLT expectations or tests too. Informational constraints exist in some engines; on Delta, prefer enforced ones you have verified.

Code

sql
-- Key must always be present
ALTER TABLE main.silver.orders
ALTER COLUMN order_id SET NOT NULL;

-- Simple domain rule on write
ALTER TABLE main.silver.orders
ADD CONSTRAINT orders_amount_positive CHECK (amount > 0);

What this code does

  1. It rejects rows with a null order_id.
  2. It rejects rows with amount <= 0.
  3. Future MERGE and INSERT have to pass both rules.

Trap

Adding a CHECK and assuming it also enforces unique order_id.

Q-DBX-087 Query system billing tables coding medium

Answer

system.billing.usage shows DBU usage. Filter by date, workspace, sku, and tags. This is how you prove which job spent the money. You need access to the system catalog.

Explanation

Cost tags only help if you query them. Join identity metadata to see the service principal. Finance wants daily rollups, not a screenshot of one cluster. Storage is a separate cloud bill.

Code

sql
-- Chargeback: DBU usage for the last 7 days
SELECT
  usage_date,
  sku_name,
  identity_metadata.run_as AS run_as,   -- user or service principal
  custom_tags['team'] AS team,          -- cost tag from the job or warehouse
  custom_tags['env'] AS env,
  sum(usage_quantity) AS dbus
FROM system.billing.usage
WHERE usage_date >= current_date() - 7
GROUP BY 1, 2, 3, 4, 5
ORDER BY dbus DESC;

What this code does

  1. It reads the last 7 days of Databricks usage.
  2. It groups by sku, identity, and cost tags.
  3. It sorts so the most expensive lines show first.

Trap

Looking only at the cluster UI for one day and saying the platform has no cost data.

Q-DBX-088 Enable deletion vectors and purge them coding medium

Answer

Enable delta.enableDeletionVectors. Deletes and MERGE then mark rows instead of rewriting whole files. REORG ... APPLY (PURGE) physically removes marked rows later.

Explanation

This cuts write amplification and DBU on MERGE-heavy silver tables. Readers apply the marks, so too many leftover vectors can slow scans. OPTIMIZE and REORG clean up. Check UniForm and external Iceberg readers before you rely on this in a shared lake.

Code

sql
ALTER TABLE main.silver.orders
SET TBLPROPERTIES ('delta.enableDeletionVectors' = 'true');

-- Later: rewrite files and drop the deletion-vector marks
REORG TABLE main.silver.orders APPLY (PURGE);

What this code does

  1. It turns on row-level delete marks for future writes.
  2. MERGE and DELETE can avoid rewriting whole Parquet files.
  3. REORG rewrites clean files and purges those marks.

Trap

Expecting VACUUM alone to remove deletion-vector marks from current files.

Q-DBX-089 Create a catalog and schema with grants coding medium

Answer

Create the catalog, then the schema, then grant USE plus create rights to a group. Do this in Git or a bootstrap job. Do not let every user create catalogs.

Explanation

Environment layout is a governance decision. dev and prod catalogs are easier than mixing in one schema. The creating user becomes owner unless you transfer ownership to a group. Production ownership should be a group or service principal.

Code

sql
CREATE CATALOG IF NOT EXISTS main
COMMENT 'Production Unity Catalog data';

CREATE SCHEMA IF NOT EXISTS main.silver
COMMENT 'Conformed tables';

GRANT USE CATALOG ON CATALOG main TO `data_engineers`;
GRANT USE SCHEMA, CREATE TABLE ON SCHEMA main.silver TO `data_engineers`;

-- Job identity can write silver, not create new catalogs
GRANT USE SCHEMA, CREATE TABLE ON SCHEMA main.silver TO `jobs-orders-sp`;

What this code does

  1. It creates the main catalog and silver schema.
  2. It lets engineers use that schema and create tables.
  3. It lets the job service principal create tables there too.

Trap

Leaving a personal user as the owner of the prod catalog.

Q-DBX-090 Share a table with Delta Sharing coding medium

Answer

Create a share, add the table, and grant it to a recipient. Recipients read live data without a copy. Provider compute and storage still pay when they query.

Explanation

This is the governance-friendly alternative to exporting parquet. You can share tables or views. Row filters still matter if the recipient should not see every tenant. Audit who has the recipient credential.

Code

sql
-- Provider side
CREATE SHARE IF NOT EXISTS sales_share;

ALTER SHARE sales_share
ADD TABLE main.gold.daily_revenue;

-- Recipient already registered in Unity Catalog
GRANT SELECT ON SHARE sales_share TO RECIPIENT acme_partner;

What this code does

  1. It creates a share object named sales_share.
  2. It adds one gold table to that share.
  3. It lets the acme_partner recipient SELECT those live rows.

Trap

Copying gold parquet to a public bucket because sharing "looked slower to set up."

Q-DBX-091 Create a foreign catalog for Lakehouse Federation coding medium

Answer

Create a connection with secrets, then a foreign catalog. Query the remote database through Unity Catalog names. Pushdown filters. Do not replace bronze ETL for huge daily facts.

Explanation

Federation is for exploration and light joins. The source database still enforces its own passwords. UC adds another GRANT layer. If Spark pulls the whole remote table, you will pay twice and wait.

Code

sql
CREATE CONNECTION IF NOT EXISTS pg_orders
TYPE POSTGRESQL
OPTIONS (
  host 'db.example.com',
  port '5432',
  user secret('prod-kv', 'pg-user'),
  password secret('prod-kv', 'pg-password')
);

CREATE FOREIGN CATALOG IF NOT EXISTS pg_orders
USING CONNECTION pg_orders;

-- Three-level name, but the data still lives in Postgres
SELECT order_id, amount
FROM pg_orders.public.orders
WHERE order_date = DATE '2026-09-15'
LIMIT 100;

What this code does

  1. It stores a Postgres connection that reads secrets at runtime.
  2. It mounts that database as a foreign catalog.
  3. It queries one day of remote orders through Unity Catalog.

Trap

Joining a 5 TB Postgres table to Delta with no filter and calling it a lakehouse pipeline.

Q-DBX-092 Tag a table and a job for cost and governance coding easy

Answer

Set Unity Catalog tags on the table for PII and domain. Set Spark or job tags for team and environment chargeback. Policies should require the cost tags. Classification tags are not billing tags.

Explanation

Finance reads compute tags from usage tables. Governance reads UC tags in Catalog Explorer. You need both. A job with no team tag is how bills go unexplained. Do not put secrets in tags.

Code

sql
-- Governance / discovery tags on the table
ALTER TABLE main.silver.orders
SET TAGS ('domain' = 'orders', 'pii' = 'none', 'layer' = 'silver');

COMMENT ON TABLE main.silver.orders IS 'Conformed orders. Source of gold revenue.';

-- Session tags that show up on compute usage (job cluster YAML is better)
SET spark.databricks.clusterUsageTags.team = 'orders';
SET spark.databricks.clusterUsageTags.env = 'prod';

What this code does

  1. It labels the table for catalog search and PII policy.
  2. It adds a human comment for the next engineer.
  3. It stamps team and env on compute usage for chargeback.

Trap

Tagging only the table, then asking finance why the SQL warehouse has no owner.