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.
Interview Q&A
๐งฑ Databricks
Delta, Unity Catalog, jobs, and lakehouse production.
92 questions ยท 50 theory ยท 42 coding
Q-DBX-001 What is Databricks?
Answer
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?
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?
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?
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?
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
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?
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
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?
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
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?
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
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?
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
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
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?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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?
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)?
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?
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?
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?
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?
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?
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?
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
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?
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?
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?
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?
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?
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
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?
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?
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
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
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?
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?
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?
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?
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?
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?
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
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
-- 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
- It creates a managed Delta table in the
maincatalog andsilverschema. - It declares types and NOT NULL on keys.
- 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
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
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
- It makes
customer_skan auto number. - It still stores
customer_idso CDC can match the same person later. - 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
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
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
- It stores
event_timeas the raw timestamp. - It always computes
event_datefrom that timestamp. - It clusters by
event_dateso 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
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
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
- It creates a Delta table with liquid clustering on customer and date.
OPTIMIZErewrites files so rows with the same keys sit together.- 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
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
-- 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
- It ranks landing rows so each
order_idappears once. - It updates silver only when the source timestamp is newer.
- It inserts keys that silver does not have yet.
- 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
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
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
- It matches on
order_id. - It deletes silver rows whose CDC event is a delete.
- It updates remaining matches when the event is newer.
- 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
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
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
- It ranks landing rows inside each
order_id. - It keeps rank 1, the newest event.
- It updates silver when the event is newer.
- It inserts brand-new orders.
Trap
Calling dropDuplicates(["order_id"]) and assuming Spark kept the latest amount.
Q-DBX-058 Compact files with OPTIMIZE
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
-- 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
- It reads the current small files.
- It writes fewer larger files.
- 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
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
-- 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
- It compacts files.
- It sorts data along
customer_idandorder_date. - 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
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
-- 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
- Dry run lists candidate files and deletes nothing.
- The real VACUUM deletes files older than 168 hours that the current table no longer needs.
- 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
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
-- 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
- It disables the safety check that blocks short retention.
- It deletes unreferenced files with zero hours of grace.
- 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
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
-- 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
- History shows version numbers and timestamps.
- The first SELECT reads version 12 without changing the table.
- 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
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
-- 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
- It prints recent commits.
- It filters to MERGE so you can find the bad load.
- 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
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
-- 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
- It checks history so you pick the right version.
- It restores data files from version 12 as the current table.
- 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
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
-- 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
- It enables CDF on future commits.
- It reads the change rows between two versions.
_change_typetells 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
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
-- 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
- It reads JSON files from a governed volume.
- It inserts only files not already tracked by this table.
- 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
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
(
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
- It incrementally discovers new JSON files.
- It infers and evolves schema from a durable schema location.
- It writes new rows to a Delta table with exactly-once checkpoints.
availableNowprocesses pending files and stops.
Trap
Sharing one checkpoint path across two Auto Loader jobs.
Q-DBX-068 DLT / Lakeflow expectations sketch
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
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
- It lands files into a bronze streaming table.
- It builds silver from bronze.
- Null
order_idrows are dropped. - 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
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
-- 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
- It lets analysts open the
maincatalog. - It lets them open the
goldschema. - It lets them read one gold table.
- 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
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
# 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
- It turns AQE on for this session.
- It avoids a hard-coded 200 shuffle partitions.
- It keeps short VACUUM blocked.
- 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
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
-- 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
- Deep clone writes a standalone copy.
- Shallow clone creates a new table name that points at existing files.
- 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
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
-- 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
- It writes Delta transaction log files beside the parquet files.
- It records
order_datepartitions in that log. - 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
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
-- 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
- It creates a governed volume named
landing. - It grants the job service principal file read access.
- 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
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
# 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
- It reads the latest JSON files.
- It appends rows to bronze.
- If
coupon_codeis 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
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
# 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
- It selects a smaller set of columns.
- It overwrites a sandbox table.
- 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
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
-- 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
- It hashes email and address so unchanged customers are skipped.
- MERGE closes the current row when the hash changes.
- MERGE inserts customers who never existed.
- 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
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
(
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
- It reads new bronze rows as a stream.
- It writes them into a silver Delta table.
- It stores offsets under a volume checkpoint so reruns do not duplicate.
availableNowprocesses 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
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
# 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
- It fetches the JDBC URL and password from a secret scope.
- It reads the source database without putting the password in Git.
- 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
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
-- 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
- It creates a masking function in the gold schema.
- It attaches that function to
email. - 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
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
-- 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
- It declares new liquid clustering columns.
- OPTIMIZE rewrites data files toward those columns.
- 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
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
-- 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
- It aggregates completed silver orders by date.
- It writes a managed Delta gold table.
- It clusters by
order_datefor dashboard filters.
Trap
CTAS into gold with no WHERE on status, then calling it revenue.
Q-DBX-082 Replace one partition or date slice
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
-- 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
- It deletes the current 2026-09-15 slice from silver as part of the write.
- It inserts the reprocessed rows for that date.
- 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
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
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
- Each microbatch is a DataFrame of new bronze rows.
- The function keeps the newest event per
order_id. - MERGE updates silver or inserts new keys.
- 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
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
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
- It finds the latest silver version.
- It reads CDF rows after the last applied version.
- It keeps inserts, update postimages, and deletes.
- 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
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
-- 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
- It computes stats on every column.
- The optimizer can pick better joins later.
DESCRIBE DETAILshows 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
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
-- 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
- It rejects rows with a null
order_id. - It rejects rows with amount <= 0.
- 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
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
-- 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
- It reads the last 7 days of Databricks usage.
- It groups by sku, identity, and cost tags.
- 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
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
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
- It turns on row-level delete marks for future writes.
- MERGE and DELETE can avoid rewriting whole Parquet files.
- 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
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
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
- It creates the
maincatalog andsilverschema. - It lets engineers use that schema and create tables.
- 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
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
-- 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
- It creates a share object named
sales_share. - It adds one gold table to that share.
- It lets the
acme_partnerrecipient 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
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
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
- It stores a Postgres connection that reads secrets at runtime.
- It mounts that database as a foreign catalog.
- 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
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
-- 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
- It labels the table for catalog search and PII policy.
- It adds a human comment for the next engineer.
- 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.
No questions match. Clear search or pick All.