How to query your CUR in Athena without a Glue crawler (2026)

Akal Cloud Updated 8 min read

Quick answer

Partition projection lets Athena calculate partition values from table properties rather than looking them up, which AWS says avoids the GetPartitions call to the Glue Data Catalog and removes the need to create partitions manually. A date-typed projection with a range ending in NOW covers every future billing period automatically. The catch is that every failure mode returns zero rows rather than an error, and projection applies only when the table is queried through Athena.

A Cost and Usage Report is a partitioned dataset that grows a new partition every month, forever, and that one table is usually the most-queried thing a FinOps team owns. The default way to make Athena see those partitions is a Glue crawler on a schedule. There is a better way that AWS documents, costs nothing to run, and removes the crawler entirely.

Why is a crawler the wrong tool here?

Two reasons, one about latency and one about money.

The latency one: a crawler is a scheduled job, so a partition exists in the catalog only after the crawler next runs. New billing period data lands and your queries cannot see it until the schedule catches up. The usual workarounds, running the crawler more often or firing MSCK REPAIR TABLE after every delivery, are both jobs that can fail quietly and leave you querying an incomplete month without any error.

The money one is subtler and shows up at query time. Per the Athena partition projection documentation: "normally, when processing queries, Athena makes a GetPartitions call to the AWS Glue Data Catalog before performing partition pruning. If a table has a large number of partitions, using GetPartitions can affect performance negatively."

A CUR table partitioned by account and billing period accumulates partitions as the product of both. A hundred accounts over three years is thousands of partitions, and every query pays a metadata lookup before it reads a byte.

What is partition projection?

AWS: "in partition projection, Athena calculates partition values and locations using the table properties that you configure directly on your table in AWS Glue. The table properties allow Athena to 'project', or determine, the necessary partition information instead of having to do a more time-consuming metadata lookup in the AWS Glue Data Catalog."

So you describe the shape of your partitions once, as table properties, and Athena computes the partition list in memory at query time. There is no partition registration step at all, which is why AWS lists among its benefits that it "automates partition management because it removes the need to manually create partitions in Athena, AWS Glue, or your external Hive metastore."

AWS names the situations it fits, and a CUR table hits all three:

  • "Queries against a highly partitioned table do not complete as quickly as you would like."
  • "You regularly add partitions to tables as new date or time partitions are created in your data. With partition projection, you configure relative date ranges that can be used as new data arrives."
  • "You have highly partitioned data in Amazon S3."

That middle one is the CUR case exactly. A new billing period appears every month on a predictable naming scheme, so a relative date range covers every future month without anyone touching the table again.

What does it cost?

Nothing extra. Projection is a table property, not a service. What it changes is the fixed overhead before each query, and it removes the crawler's own charges.

Athena itself bills on data scanned. Athena pricing works its example at $5 per TB scanned, and demonstrates the two levers that actually move that number: its example shows "3x savings from compression and 4x savings for reading only one column" after converting to Parquet.

Partition projection does not reduce bytes scanned by itself. Partition pruning does that, and pruning still applies: "Athena uses partition pruning for all tables with partition columns, including those tables configured for partition projection." Projection removes the catalog lookup; pruning removes the data read. You want both, and if you want a hard ceiling on what a single query can scan regardless, the per-query workgroup limit is the only control that cancels anything. A query with no partition predicate in its WHERE clause defeats pruning whatever else you have configured.

How do you configure it?

Through TBLPROPERTIES, documented across Set up partition projection and Supported types for partition projection. For a CUR laid out by account and billing period, the shape is a projected enum for accounts and a projected date for billing periods:

TBLPROPERTIES (
  'projection.enabled' = 'true',

  'projection.account.type' = 'enum',
  'projection.account.values' = '111122223333,444455556666',

  'projection.billing_period.type' = 'date',
  'projection.billing_period.range' = '2024-01,NOW',
  'projection.billing_period.format' = 'yyyy-MM',
  'projection.billing_period.interval' = '1',
  'projection.billing_period.interval.unit' = 'MONTHS',

  'storage.location.template' =
    's3://your-bucket/cur/account=${account}/billing_period=${billing_period}/'
)

NOW is what makes this maintenance-free, and it is a documented keyword rather than a convention: AWS's date range property accepts relative strings matching NOW with an optional offset such as NOW-3YEARS or NOW+1MONTH. The range end moves with the clock, so next month's partition is projected the moment its data lands. No crawler, no MSCK REPAIR TABLE, no scheduled job to monitor. One detail if you partition by hour rather than month: "projected date columns are generated in Coordinated Universal Time (UTC) at query execution time."

Two setup failures worth knowing before you try this on an existing table. The partition column has to be in the schema already: "before you add partition projection properties to an existing table, the partition column for which you are setting up partition projection properties must already exist in the table schema… AWS Glue does not perform this step for you automatically." And a partially configured table fails loudly rather than silently, which is a relief given everything else here does not: setting projection.enabled to true while leaving a column unconfigured returns HIVE_METASTORE_ERROR: Table ... is configured for partition projection, but the following partition columns are missing projection configuration.

The storage.location.template line matters if your prefixes are not the Hive default. AWS notes that "by default, Athena builds partition locations using the form s3://amzn-s3-demo-bucket/<table-root>/partition-col-1=<val>/, but if your data is organized differently, Athena offers a mechanism for customizing this path template."

AWS lists the partition shapes projection handles well: integers, dates, "enumerated values… such as airport codes or AWS Regions", and specifically "AWS service logs", which "typically have a known structure whose partition scheme you can specify in AWS Glue and that Athena can therefore use for partition projection". A CUR export and VPC Flow Logs delivered to S3 are both in that last category.

What breaks silently?

This is the part to read before switching a production table over, because every failure mode here returns zero rows rather than an error. AWS's own considerations list:

BehaviourWhat AWS says
Catalog partitions are ignored "Enabling partition projection on a table causes Athena to ignore any partition metadata registered to the table in the AWS Glue Data Catalog or Hive metastore."
Missing partitions are projected anyway "If a projected partition does not exist in Amazon S3, Athena will still project the partition. Athena does not throw an error, but no data is returned."
Out-of-range queries return nothing "Queries for values that are beyond the range bounds defined for partition projection do not return an error. Instead, the query runs, but returns zero rows."
Too many empty partitions is slow "If more than half of your projected partitions are empty, it is recommended that you use traditional partitions."
SHOW PARTITIONS is misleading "Because partition projection is a DML-only feature, SHOW PARTITIONS does not list partitions that are projected by Athena but not registered in the AWS Glue catalog."
Views do not inherit it "Athena does not use the table properties of views as configuration for partition projection."

The second and third rows together are the trap worth naming. Set your range start to 2025-01 and query January 2024, and you get a clean, fast, empty result set that looks exactly like "we had no spend that month". A crawler-backed table would at least have told you the partition did not exist. Whoever owns the table has to own the range.

The empty-partition warning also has a real CUR consequence: if you project enum account values for every account that has ever existed, and most accounts only have data for part of the range, you can cross AWS's half-empty threshold. Project the accounts that actually have data.

There is a harder ceiling behind that, and it is the one to check before projecting accounts at all in a large organisation. AWS recommends "limiting the use of enum based partition projections to a few dozen or less", and explains why: "although there is no specific limit for enum projections, the total size of your table's metadata cannot exceed the AWS Glue limit of about 1 MB when gzip compressed. Note that this limit is shared across key parts of your table like column names, location, storage format, and others."

A CUR table has a lot of columns competing for that budget: CUR 2.0 has 125 possible columns. An organisation with a few hundred accounts is exactly the case AWS is warning about, and its suggested escape is to reduce cardinality: "consider an alternative approach such as bucketing into a smaller number of unique values in a surrogate field." In practice that often means projecting the billing period only and leaving account as an ordinary non-projected column, or splitting the table per organisational unit.

Does it work outside Athena?

No, and this is the most important architectural caveat: "partition projection is usable only when the table is queried through Athena. If the same table is read through another service such as Amazon Redshift Spectrum, Athena for Spark, or Amazon EMR, the standard partition metadata is used."

So a table shared between an Athena dashboard and a Redshift Spectrum pipeline will behave differently in each. The Spectrum side falls back to catalog partitions, which projection told you to stop maintaining, and it will see nothing. If more than Athena reads your CUR table, either keep catalog partitions maintained as well, or accept that projection is an Athena-only optimisation and scope it to an Athena-only table.

What does it not solve?

  • Bytes scanned. Projection removes metadata lookups, not data reads. Column selection and a partition predicate in the WHERE clause do that. SELECT * against a CUR is expensive no matter how the partitions are computed.
  • Schema drift. For legacy CUR, the CUR troubleshooting guide warns "the columns that AWS includes in your report depend on your AWS usage", so columns appear and disappear between months. CUR 2.0's fixed schema is the fix for that, not projection.
  • Knowing which number is right. A fast query over the wrong cost column is still wrong; see why Cost Explorer and your CUR disagree.
  • Allocation. Projection makes the table queryable. Whether the rows carry an owner is a separate problem, covered in why cost allocation tags show up empty.

We run partition projection over the CUR, FOCUS and Kubernetes metrics tables in production, with no crawlers anywhere in the pipeline. The operational argument is simpler than the performance one: a crawler is a scheduled job that can fail, and a table property cannot.

See this on your own bill

Akal Cloud connects in about two minutes and shows the same numbers against your real AWS accounts.

Get started on AWS Marketplace

Related reading

  • How to stop one Athena query from scanning your whole CUR (2026)

    Athena's two workgroup cost controls behave completely differently: the per-query limit cancels a query, the per-workgroup limit only alerts. Plus the charge for cancelled queries and the partial results they leave in S3.

  • How to see Amazon Bedrock cost per team (2026)

    Bedrock inference is not a resource you tag, so it has no owner column. The two features that give it one: IAM principal cost allocation in CUR 2.0, and application inference profiles. What each one answers, and why CloudWatch cannot answer it at all.

  • ECS split cost allocation data, and how it differs from EKS (2026)

    Split cost allocation data covers ECS tasks as well as Kubernetes pods, but ECS gets no service-generated tags, no GPU splitting, and the same row multiplier. What you get free on each side, and the row count formula AWS publishes.

  • Why your cost allocation tags show up empty in the CUR (2026)

    Tagging a resource and activating a cost allocation tag are two different acts, and only one of them reaches your bill. The two 24-hour delays, exactly what a backfill can and cannot recover, and why a CUR 2.0 query written against user:team finds nothing.