How does an AWS Glue crawler decide which parts of an S3 key prefix become partition columns?
answer
- look at the folder names, not the files
- key=value is the magic shape
- no name means a numbered stand-in
- consistency across branches decides one table or many
- depth order sets the column order
basics
~20 sIt walks the folder levels below its target prefix. Hive-style folders named key=value become a partition column called key; any other consistent folder level becomes an unnamed column partition_0, partition_1 and so on, in depth order.
solid answer
~50 sAn AWS Glue crawler treats the sub-folder levels beneath its S3 target as candidate partition levels. If a level is **Hive-style** — `dt=2026-08-20`, `region=eu` — the crawler names the partition column after the key and uses the text after `=` as the value. If the folders carry no `key=`, as in `2026/08/20/`, the crawler still recognises the levels but has no names for them, so it invents `partition_0`, `partition_1`, `partition_2` by depth. Queries then have to filter on `partition_0 = '2026'`, which is a strong practical argument for writing Hive-style prefixes in the first place. The crawler only does this when the structure is *consistent*: if some files sit directly under the target and others three levels down, or if sub-folders hold incompatible schemas or different file formats, the crawler is likely to emit several tables instead of one partitioned table.
code
text · 9 lines-- Hive-style: named partition columns
s3://lake/events/dt=2026-08-20/region=eu/part-0000.parquet
partition keys: dt string, region string
query: WHERE dt = '2026-08-20' AND region = 'eu'
-- non-Hive: numbered stand-ins
s3://lake/logs/2026/08/20/part-0000.parquet
partition keys: partition_0 string, partition_1 string, partition_2 string
query: WHERE partition_0 = '2026' AND partition_1 = '08'go deeper
Know the key=value convention and that following it gives you named partition columns instead of partition_0, partition_1.
Explain how the crawler descends folder levels, when it groups them into one table versus many, and what mixed formats or ragged depth do to that decision.
Set the layout standard before data lands: partition grain matched to query filters, one format per prefix, staging paths excluded, and partition metadata inheriting from the table.
Own partitioning as a cost and contract decision across the lake — cardinality versus scan efficiency, and how much layout you are willing to freeze because downstream queries now depend on the column names.
## The rule A crawler's S3 target is the root of a table. Everything below it is structure. The crawler descends, and for each folder level it asks two questions: does every branch at this level look the same, and is the level named in Hive style? **Hive-style** means a folder whose name is `key=value`. That convention comes straight from the Hive metastore model the Data Catalog implements, and it is the only naming the tooling can interpret. A path of ``` s3://lake/events/dt=2026-08-20/region=eu/part-0000.parquet ``` yields a table `events` with partition keys `dt` and `region`, and a partition record whose values are `2026-08-20` and `eu`, pointing at that folder. Without the `key=` prefix, as in ``` s3://lake/logs/2026/08/20/part-0000.parquet ``` the crawler still sees three consistent levels but has nothing to name them after, so it produces partition columns `partition_0`, `partition_1` and `partition_2` in depth order. The table works, but every downstream query reads `WHERE partition_0 = '2026' AND partition_1 = '08'`, which is opaque, easy to get wrong, and impossible to change later without rewriting the layout or hand-editing the table. Partition column values arrive as strings; if you want dates or integers you either name the columns yourself in DDL or handle the cast in queries. ## Why one prefix sometimes becomes many tables The crawler's second job is **table grouping**: deciding whether the folders it found are partitions of one table or independent tables. It groups them into one table when their schemas are compatible and the structure is uniform. It splits them when they are not. Common causes of an unwanted split: - **Mixed formats** under one prefix — some folders JSON, some Parquet. Those cannot share a SerDe, so they cannot share a table. - **Incompatible schemas** — one folder's `amount` is an integer and another's is a quoted string, or column sets diverge sharply. - **Ragged depth** — files directly under the root alongside files two levels down. The crawler cannot decide what level the table starts at. - **Writer debris** — `_temporary/`, `_SUCCESS`, `.spark-staging` folders and zero-byte folder-marker objects can be picked up as if they were partitions. The crawler configuration option **"Create a single schema for each S3 path"** (in the API, the grouping policy `CombineCompatibleSchemas`) tells the crawler to prefer one table per path and merge schemas that differ only slightly, which fixes most accidental splits. Exclude patterns fix the debris. Ragged depth is a layout problem and has to be fixed by the writer. ## Two more knobs that bite **Table level.** For nested layouts you can tell the crawler how many folder levels down the *table root* lives, which prevents it from mistaking a top-level grouping folder for a partition key — useful when one bucket holds many datasets. **Partition metadata inheritance.** Each partition record carries its own storage descriptor. If the table's SerDe or column types are updated but partitions keep old ones, engines raise a schema-mismatch error at read time. The crawler option "Update all new and existing partitions with metadata from the table" (partition `AddOrUpdateBehavior` = `InheritFromTable`) makes partitions follow the table definition and heads that off. ## What good looks like Write Hive-style prefixes from the start: `dt=YYYY-MM-DD`, and add a second key only when queries genuinely filter on it. Keep the cardinality sane — a partition per minute produces millions of catalog records and slow query planning, while a partition per day with reasonably sized files is easy on both the catalog and the scan. Keep one dataset per prefix and one format per dataset. Do those three things and partition inference is boring, which is exactly what you want from it. If you inherit a non-Hive layout you cannot rewrite, you are not stuck with `partition_0`: define the table yourself with `CREATE EXTERNAL TABLE` naming the partition columns, register partitions with explicit `ADD PARTITION … LOCATION` statements, and stop crawling that prefix.
- You inherit a bucket laid out as year/month/day and cannot rewrite it. How do you avoid partition_0?Stop crawling that prefix and define the table yourself: `CREATE EXTERNAL TABLE … PARTITIONED BY (year string, month string, day string) LOCATION 's3://…'`, then register partitions with explicit `ALTER TABLE … ADD PARTITION (year='2026', month='08', day='20') LOCATION '…'`. Athena partition projection with a location template is the other option and needs no partition records at all.
- What actually goes wrong if you partition by hour on a large table?Partition count explodes — tens of thousands per year per table — so catalog lookups during query planning get slow and crawler runs get expensive, while each partition holds files too small to scan efficiently. Partition on the grain queries actually filter by, and let file size rather than folder count carry the parallelism.
- Why did one crawler over one prefix produce a dozen tables?Because the sub-folders were not compatible: mixed file formats, diverging schemas, or files sitting at inconsistent depths under the target. Enabling "Create a single schema for each S3 path" merges near-compatible schemas into one table, and exclude patterns remove writer debris such as `_temporary/`, but genuinely mixed formats have to be separated in S3.
Hive-style folders are labelled drawers; plain numeric folders are unlabelled ones. The crawler can still count the drawers, but all it can call them is first, second and third.
saying these in an interview costs you the question
- Expecting meaningful column names from year/month/day folders
- Assuming partition values are typed as dates or integers
- Believing a crawler always produces exactly one table per prefix
- Partitioning by a high-cardinality column because it is in the WHERE clause
- Ignoring _temporary and staging folders left by the writer