The BigQuery provider enables Airflow to execute queries, load data, and manage datasets in Google BigQuery. It wraps the BigQuery Python client library.
Key Insight: Partition pruning reduces query scan from O(N) to O(N/P) where P is partition count. Always include partition columns in WHERE clauses.
Operator Selection Guide
Operator
Use Case
Key Parameters
BigQueryInsertJobOperator
Execute any BQ job
configuration, location
GCSToBigQueryOperator
Load GCS files to BQ
bucket, source_objects
BigQueryCopyTableOperator
Copy between tables
source_project_dataset_table
BigQueryCreateDatasetOperator
Create dataset
dataset_id, project_id
BigQueryTableExistenceSensor
Wait for table
project_id, dataset_id, table_id
Partitioning Strategies
Strategy
Best For
Query Pruning
Cost Impact
DAY
High-cardinality time series
Excellent
Lowest
MONTH
Medium-cardinality aggregates
Good
Low
YEAR
Low-cardinality historical
Moderate
Medium
HOUR
Real-time analytics
Excellent
Lowest
INTEGER_RANGE
Non-temporal ranges
Good
Low
Connection Setup
# Airflow connection for BigQuery
# Connection ID: google_cloud_default
# Connection Type: Google Cloud
# Project ID: my-gcp-project
# Keyfile JSON: Path to service account key
# Extra: {"scope": "https://www.googleapis.com/auth/bigquery"}
Query Execution
from datetime import datetime, timedelta
from airflow import DAG
from airflow.providers.google.cloud.operators.bigquery import (
BigQueryInsertJobOperator,
)
with DAG(
dag_id='bigquery_etl',
start_date=datetime(2024, 1, 1),
schedule_interval='@daily',
catchup=False,
tags=['bigquery', 'etl'],
) as dag:
create_table = BigQueryInsertJobOperator(
task_id='create_partitioned_table',
configuration={
'query': {
'query': """
CREATE TABLE IF NOT EXISTS `project.dataset.orders`
(
order_id INT64,
customer_id INT64,
amount NUMERIC,
order_date DATE
)
PARTITION BY order_date
CLUSTER BY customer_id
""",
'useLegacySql': False,
}
},
location='US',
)
load_data = BigQueryInsertJobOperator(
task_id='load_from_gcs',
configuration={
'load': {
'sourceUris': ['gs://data-lake/raw/orders/*.parquet'],
'destinationTable': {
'projectId': 'project',
'datasetId': 'dataset',
'tableId': 'stg_orders',
},
'sourceFormat': 'PARQUET',
'writeDisposition': 'WRITE_TRUNCATE',
'timePartitioning': {
'type': 'DAY',
'field': 'order_date',
},
'clustering': {
'fields': ['customer_id'],
},
}
},
location='US',
)
transform = BigQueryInsertJobOperator(
task_id='transform_orders',
configuration={
'query': {
'query': """
INSERT INTO `project.dataset.fct_orders`
SELECT
order_id,
customer_id,
SUM(amount) as total_amount,
COUNT(*) as order_count,
MIN(order_date) as first_order_date
FROM `project.dataset.stg_orders`
WHERE order_date = DATE('{{ ds }}')
GROUP BY order_id, customer_id
""",
'useLegacySql': False,
}
},
location='US',
)
create_table >> load_data >> transform