When to use this
A pipeline failed, row counts look wrong or someone asks, "What happened to this table?" These read-only commands answer most first questions. They're safe to run on production tables.
Every example uses main.sales.orders. Replace it with your own table.
What is this table?
| Question | Command |
|---|---|
| Columns and types | DESCRIBE TABLE main.sales.orders |
| Owner, location, type, comments | DESCRIBE TABLE EXTENDED main.sales.orders |
| Size, files, partitions | DESCRIBE DETAIL main.sales.orders |
| How it was created | SHOW CREATE TABLE main.sales.orders |
| Properties | SHOW TBLPROPERTIES main.sales.orders |
| Tables in a schema | SHOW TABLES IN main.sales |
How big is it?
DESCRIBE DETAIL main.sales.orders;
Useful columns in the result:
| Column | Meaning |
|---|---|
format | Should be delta |
location | Where the files are stored |
numFiles | Number of data files in the current version |
sizeInBytes | Size of the current version |
partitionColumns | How it's partitioned |
clusteringColumns | Liquid clustering keys, if any |
lastModified | Last time it changed |
minReaderVersion, minWriterVersion | Protocol versions |
tableFeatures | Features that older readers may not support |
Work out the average file size:
d = spark.sql("DESCRIBE DETAIL main.sales.orders").first()
print(f"{d.numFiles} files, {d.sizeInBytes/1024**3:.1f} GB, "
f"{d.sizeInBytes/max(d.numFiles,1)/1024**2:.1f} MB per file")
Tens of thousands of files with an average of a few MB suggest a small-file problem.
sizeInBytes covers only the current version. Old versions kept for time travel use extra storage that isn't shown here.
What changed, and who did it?
DESCRIBE HISTORY main.sales.orders LIMIT 20;
Key columns:
| Column | Meaning |
|---|---|
version | The version number |
timestamp | When the change happened |
userName | Who ran it |
operation | WRITE, MERGE, DELETE, OPTIMIZE, VACUUM START and so on |
operationParameters | Details, such as the write mode and predicates |
operationMetrics | Rows and files added, updated or removed |
job, notebook | The job or notebook that ran it |
clusterId | The compute used |
To query history, use a view of the output with Python:
from pyspark.sql.functions import col
h = spark.sql("DESCRIBE HISTORY main.sales.orders")
(h.select("version", "timestamp", "userName", "operation",
col("operationParameters.mode").alias("mode"),
col("operationMetrics.numOutputRows").alias("rows_out"),
col("operationMetrics.numTargetRowsDeleted").alias("rows_deleted"),
col("job.jobId").alias("job"))
.orderBy(col("version").desc())
.show(20, truncate=False))
Common questions:
| Question | Look for |
|---|---|
| Who deleted rows? | operation = 'DELETE', and numDeletedRows in metrics |
| Why did it drop to almost nothing? | WRITE with mode = Overwrite |
| Did a MERGE do anything? | numTargetRowsInserted, numTargetRowsUpdated |
| When did maintenance last run? | OPTIMIZE, VACUUM END |
| Did the job run at all today? | No new versions since yesterday |
Compare two versions
SELECT 'now' AS v, count(*) FROM main.sales.orders
UNION ALL
SELECT 'v41', count(*) FROM main.sales.orders VERSION AS OF 41;
Find rows that exist in one version and not in the other:
SELECT * FROM main.sales.orders VERSION AS OF 41
EXCEPT
SELECT * FROM main.sales.orders;
Or go by time:
SELECT count(*) FROM main.sales.orders TIMESTAMP AS OF '2026-11-05T22:00:00';
If this fails with a missing version or file, retention may have removed the data. See time travel retention.
Who can access it?
SHOW GRANTS ON TABLE main.sales.orders;
SHOW GRANTS `priya@contoso.com` ON TABLE main.sales.orders;
SHOW GRANTS ON SCHEMA main.sales;
SHOW GRANTS ON CATALOG main;
A user needs USE CATALOG and USE SCHEMA as well as SELECT. See the grants cheat sheet.
What uses this table?
Unity Catalog records lineage in system tables, if they're enabled:
SELECT source_table_full_name, target_table_full_name, entity_type, max(event_time) AS last_seen
FROM system.access.table_lineage
WHERE source_table_full_name = 'main.sales.orders'
OR target_table_full_name = 'main.sales.orders'
GROUP BY 1, 2, 3
ORDER BY last_seen DESC;
More in lineage and impact analysis.
Check its properties
SHOW TBLPROPERTIES main.sales.orders;
SHOW TBLPROPERTIES main.sales.orders ('delta.logRetentionDuration');
| Property | Controls |
|---|---|
delta.logRetentionDuration | How long table history stays |
delta.deletedFileRetentionDuration | How long removed files stay before VACUUM can delete them |
delta.autoOptimize.autoCompact | Automatic small-file compaction |
delta.enableChangeDataFeed | Row-level change feed |
delta.enableDeletionVectors | Deletion vectors |
delta.columnMapping.mode | Column rename and drop support |
Quick data checks
SELECT count(*) AS total, count(DISTINCT order_id) AS distinct_ids,
count(*) - count(order_id) AS null_ids,
min(order_date) AS first_date, max(order_date) AS last_date
FROM main.sales.orders;
SELECT order_date, count(*)
FROM main.sales.orders
WHERE order_date >= current_date() - 14
GROUP BY 1 ORDER BY 1;
The first finds duplicates and nulls. The second shows gaps or dips by day.
Careful: these commands change things
Inspection is read-only, but these look similar and aren't:
| Command | Effect |
|---|---|
RESTORE TABLE | Changes the current table to an old version |
VACUUM | Deletes old files permanently, and limits time travel |
OPTIMIZE | Rewrites files and uses compute |
ALTER TABLE ... SET TBLPROPERTIES | Changes behavior |
Caution
Don't run
VACUUMwhile you're investigating a data problem. It can remove the old versions you may need for recovery.
Prevent it next time
- Include job and table names in run logs, so history lines up with your scheduler.
- Save a dated copy of
DESCRIBE DETAILfor key tables, to see trends in size and file count. - Learn the basics of Delta tables if you're new to them.