Based on the DEA-C01 exam guideReport an error
SELECT *, filter on the partition key).Amazon Athena has no infrastructure to provision. When you submit a query, Athena runs a Trino-based engine (engine version 3) on your behalf.
-- Define an external table that points to an S3 path
CREATE EXTERNAL TABLE orders (
order_id BIGINT,
amount DOUBLE,
status STRING
)
PARTITIONED BY (dt STRING)
STORED AS PARQUET
LOCATION 's3://datalake-clean/orders/';DROP TABLE, the files on S3 remain.| Item | Details |
|---|---|
| Basic unit | Amount of data the query scanned (per TB). Compressed files are counted at their compressed size |
| Minimum charge | 10 MB per query |
| DDL | DDL such as CREATE TABLE and ALTER TABLE ADD PARTITION involves no scan and is not billed |
| Canceled queries | Billed for what was scanned up to the point of cancellation |
| Accompanying costs | S3 storage and requests, the Glue Data Catalog, Lambda invocations for federated queries |
| Alternative pricing | Provisioned capacity (reserve DPUs and pay per hour, with no per-scan charge) |
💡 Related Theory: In Athena, cost optimization and performance optimization point the same way, because both come down to "reading less from S3." In a service such as Redshift where compute is reserved in advance, the bill stays the same no matter how frugal the queries are, but with Athena the bill shrinks in proportion to the queries you fix.
Even for the same question, the amount read varies greatly with the shape of the data. Take a query that sums two columns over one day of orders as an example.
| Data shape | Range Athena reads |
|---|---|
| CSV, no partitions | Every byte of the whole table |
| CSV + gzip | The whole table (at compressed size). A single file cannot be split for reading, so parallelism also drops |
| CSV, date partitions | All columns under that date prefix |
| Parquet, date partitions | Only the two needed columns under that date prefix |
| Parquet (sorted), date partitions + an extra filter | On top of the above, skips the ranges that the min/max statistics rule out |
-- Load partition metadata (when the paths are Hive-style)
MSCK REPAIR TABLE orders;
-- Or add an individual partition
ALTER TABLE orders ADD PARTITION (dt='2026-06-26')
LOCATION 's3://datalake-clean/orders/dt=2026-06-26/';
-- Pruning works: only the dt prefix is scanned
SELECT status, COUNT(*) FROM orders
WHERE dt = '2026-06-26' GROUP BY status;MSCK REPAIR TABLE scans S3 and only adds partitions. It does not remove partitions that were deleted, and when there are many partitions it can take a long time and time out, ending with only some of them registered. For adding one partition every day, ALTER TABLE ADD PARTITION is the right tool.ALTER TABLE orders SET TBLPROPERTIES (
'projection.enabled' = 'true',
'projection.dt.type' = 'date',
'projection.dt.range' = '2024-01-01,NOW',
'projection.dt.format' = 'yyyy-MM-dd',
'storage.location.template' = 's3://datalake-clean/orders/dt=${dt}/'
);-- SELECT only the needed columns → reduces scan volume in a columnar format
SELECT order_id, amount FROM orders WHERE dt = '2026-06-26';
-- SELECT * scans every column, so avoid itLIMIT does not guarantee a smaller scan. When a sort or aggregation is involved, it reads everything and then trims.EXPLAIN to see which partitions are targeted, and after running, check the amount actually read with EXPLAIN ANALYZE or the "data scanned" figure in the query statistics.💡 Related Theory: Converting CSV to Parquet + compression applies "column projection (only the needed columns)," "compression (values of the same type sit together and shrink well)," and "statistics-based skipping" all at once. That is why format conversion is the first card played on an Athena cost problem. Partitions come next, and query fixes are layered on top.
CTAS (CREATE TABLE AS SELECT) writes query results to a new S3 location and registers it as a table.
-- Convert the CSV source into partitioned Parquet
CREATE TABLE orders_parquet
WITH (
format = 'PARQUET',
write_compression = 'SNAPPY',
partitioned_by = ARRAY['dt'],
external_location = 's3://datalake-clean/orders_parquet/'
) AS
SELECT order_id, amount, status, dt -- the partition column goes at the very end of the SELECT list
FROM orders_raw_csv
WHERE dt BETWEEN '2026-06-01' AND '2026-06-30';| Item | Details |
|---|---|
| Result format | PARQUET (default), ORC, AVRO, JSON, TEXTFILE |
| Default Parquet compression | GZIP. Specify write_compression if you want Snappy or ZSTD |
| Partition column position | Put it last in the SELECT list |
external_location | Must be an empty location. Athena does not overwrite existing data |
| Partition limit | 100 partitions written at once |
| Bucketing | Specified with bucketed_by and bucket_count |
ORDER BY | An ORDER BY used in the SELECT of a CTAS is ignored |
INSERT INTO ... SELECT.| Method | When it fits |
|---|---|
| Athena CTAS / INSERT INTO | Format conversion or saving aggregate results that SQL can handle, serverless and simple |
| Athena view | Reuse a query without writing data |
| Glue ETL | Complex transformations, schema handling, incremental loads based on bookmarks |
Iceberg table + MERGE INTO | Row-level updates and deletes, atomic commits |
Through a data source connector, you query and join sources outside S3 (RDS and Aurora, DynamoDB, Redshift, CloudWatch Logs, OpenSearch, other JDBC sources, and so on) with the same SQL.
-- Join order data on S3 with the customer master in RDS
SELECT o.order_id, c.customer_name, o.amount
FROM awsdatacatalog.sales.orders o
JOIN rds_postgres.public.customers c
ON o.customer_id = c.customer_id
WHERE o.dt = '2026-06-26';INSERT INTO is not supported on a federated source. However, reading through federation and writing to S3 with CTAS does work (a source → lake extraction pipeline).| Decision | Federated query | Move data into the lake (ETL, zero-ETL) |
|---|---|---|
| Frequency | Occasional, exploratory | Repeated, regular reports |
| Data freshness | Always current (reads the source directly) | Delayed by the load interval |
| Performance | Tied to the source's speed and the connector | Benefits of columnar format and partitions |
| Source load | Incurred on every query | Only at load time |
A workgroup is a unit for dividing query execution by team or purpose. The result location, encryption, engine version, and cost controls are attached to the workgroup.
| Control | Target | When the limit is exceeded |
|---|---|---|
| Per-query data usage limit | Scan volume of a single query (can be set from a minimum of 10 MB) | The query is canceled. What was scanned before cancellation is billed |
| Workgroup-wide data usage alert | Total scan volume of the workgroup over a period | CloudWatch alarm → SNS notification. It does not block queries automatically |
# Create a workgroup with a per-query scan limit of 10 GB
aws athena create-work-group \
--name analysts \
--configuration '{
"ResultConfiguration": {"OutputLocation": "s3://acme-athena-results/analysts/"},
"EnforceWorkGroupConfiguration": true,
"PublishCloudWatchMetricsEnabled": true,
"BytesScannedCutoffPerQuery": 10737418240
}'When the same query is repeated, you can let it reuse the previous result. When a result is reused, no scan takes place.
| Symptom | Cause | Action |
|---|---|---|
| Small result but the scan volume is the whole table | No partition key condition, row-based format | Filter on the partition key, convert to Parquet |
LIMIT 10 but it is expensive | Full scan because of sorting or aggregation | Add a partition condition |
| New data is not returned | Partition not registered | ALTER TABLE ADD PARTITION, projection |
MSCK REPAIR TABLE never finishes | Too many partitions | Switch to ADD PARTITION or projection |
| CTAS fails with a partition count error | More than 100 at once | Split into ranges with CTAS + INSERT INTO |
| CTAS fails with "data exists at the location" | external_location is not empty | Specify a new prefix or delete the existing files |
| Queries keep getting canceled | The workgroup's per-query limit exceeded | Optimize the query or adjust the limit |
| Result bucket charges keep growing | Accumulated result files, multipart fragments from failed queries | Lifecycle expiration and incomplete multipart cleanup rules |
| Federated query is slow and the production DB slows down | Large queries directly against the source | Read replica, load into the lake |
SlowDown error | Too many small files, concurrent queries | Compact files |
| Requirement | Suitable service |
|---|---|
| Occasional SQL on S3 data with no infrastructure | Athena |
| Repeating dashboards with second-level response, and complex multi-joins all the time | Redshift |
| Redshift already exists and S3 history must be joined with warehouse tables | Redshift Spectrum |
| Large-scale transformations that take hours, custom code | Glue ETL / EMR |
| Interactive analysis with Spark code, serverless | Athena for Apache Spark |
| Millisecond lookups by a single key | DynamoDB (not Athena) |
| Clue in the question | Answer |
|---|---|
| "Reduce Athena query costs" | Convert to Parquet/ORC + compression + partitioning + only the needed columns |
| "CSV to partitioned Parquet, with no infrastructure" | CTAS |
| "Tens of thousands of partitions; eliminate the registration work" | Partition projection |
| "Join S3 data with RDS or DynamoDB without moving data" | Federated query |
| "Prevent a single query from scanning excessively" | The workgroup's per-query data usage limit |
| "Separate queries, cost, and result location by team" | Workgroup |
| "Notify me when the monthly scan volume exceeds a threshold" | Workgroup-wide data usage alert (CloudWatch + SNS) |
| "The same dashboard query runs repeatedly" | Query result reuse |
| "All query results must be encrypted" | Enforce workgroup settings (override client-side settings) |
| "Row-level UPDATE and DELETE in Athena" | Iceberg tables |
Tomorrow we cover DynamoDB, an operational store rather than an analytics engine, from the viewpoint of key design and change capture that data engineers need to know.
CREATE TABLE AS SELECT. Writes query results to a new location and registers it as a table.Click a choice to reveal the answer and explanation.
Question 1
Which combination is the most effective for reducing Athena cost?
Question 2
What is the most appropriate way to convert a CSV source into a partitioned Parquet table in Athena?
Question 3
What is the most appropriate solution when there are tens of thousands of partitions and MSCK REPAIR and Glue catalog lookups have become a bottleneck?
Question 4
How can you join order data in S3 with the customer master in RDS PostgreSQL in a single SQL statement without moving the data?
Question 5
What do you use in Athena to prevent a runaway query from a particular team from scanning a huge amount of data at once?