بنقرة واحدة
etl-design
Design extract-transform-load patterns, data pipeline orchestration, and incremental loading strategies
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Design extract-transform-load patterns, data pipeline orchestration, and incremental loading strategies
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Design AI agents with appropriate capabilities, tools, and personas for specific software development tasks
Design RESTful APIs with proper resource modeling, HTTP methods, error handling, and clear contracts following REST principles
Document APIs comprehensively with signatures, parameters, return values, errors, and working code examples for developer reference
Implement robust third-party API integrations with proper authentication, error handling, and rate limiting
Apply proven architectural patterns (MVC, layered, microservices) to create maintainable systems with clear separation of concerns
Systematically reproduce, diagnose, and analyze bugs to determine root cause, assess severity, and plan fix strategy
| name | ETL Design |
| description | Design extract-transform-load patterns, data pipeline orchestration, and incremental loading strategies |
| category | database |
| required_tools | ["Read","Write","Bash"] |
Design robust ETL (Extract-Transform-Load) pipelines that move data from source systems to target systems with appropriate transformations, error handling, and performance optimization.
Extraction Strategy
Transformation Design
Loading Strategy
Orchestration
Performance Optimization
Context: Daily ETL from operational database to data warehouse
Pipeline Design:
# etl_sales_pipeline.py
import pandas as pd
from datetime import datetime, timedelta
import logging
class SalesETL:
def __init__(self, source_conn, target_conn):
self.source = source_conn
self.target = target_conn
self.logger = logging.getLogger(__name__)
def extract_incremental(self, last_run_date):
"""Extract new/modified sales records since last run"""
query = """
SELECT
sale_id,
sale_date,
customer_id,
product_id,
store_id,
quantity,
unit_price,
discount,
total_amount,
updated_at
FROM sales
WHERE updated_at > %s
ORDER BY updated_at
"""
self.logger.info(f"Extracting sales since {last_run_date}")
df = pd.read_sql(query, self.source, params=[last_run_date])
self.logger.info(f"Extracted {len(df)} records")
return df
def transform(self, df):
"""Transform and cleanse data"""
self.logger.info("Starting transformation")
# 1. Data cleansing
# Remove duplicates
df = df.drop_duplicates(subset=['sale_id'], keep='last')
# Handle nulls
df['discount'] = df['discount'].fillna(0)
# Validate data
df = df[df['quantity'] > 0] # Remove invalid quantities
df = df[df['total_amount'] >= 0] # Remove negative amounts
# 2. Data standardization
# Convert date formats
df['sale_date'] = pd.to_datetime(df['sale_date'])
df['date_key'] = df['sale_date'].dt.strftime('%Y%m%d').astype(int)
# 3. Enrichment - lookup dimension keys
df = self._lookup_dimension_keys(df)
# 4. Calculate derived fields
df['tax_amount'] = df['total_amount'] * 0.08 # 8% tax
df['cost_amount'] = self._lookup_product_cost(df['product_id'])
df['profit'] = df['total_amount'] - df['cost_amount']
# 5. Select final columns for target
target_columns = [
'sale_id', 'date_key', 'customer_key', 'product_key',
'store_key', 'quantity', 'unit_price', 'discount',
'tax_amount', 'total_amount', 'cost_amount'
]
df = df[target_columns]
self.logger.info(f"Transformation complete: {len(df)} records")
return df
def _lookup_dimension_keys(self, df):
"""Join with dimension tables to get surrogate keys"""
# Get current dimension keys
customers = pd.read_sql(
"SELECT customer_id, customer_key FROM dim_customer WHERE is_current = TRUE",
self.target
)
products = pd.read_sql(
"SELECT product_id, product_key FROM dim_product WHERE is_current = TRUE",
self.target
)
stores = pd.read_sql(
"SELECT store_id, store_key FROM dim_store WHERE is_current = TRUE",
self.target
)
# Join to get dimension keys
df = df.merge(customers, on='customer_id', how='left')
df = df.merge(products, on='product_id', how='left')
df = df.merge(stores, on='store_id', how='left')
# Handle missing keys (new customers/products/stores)
missing_customers = df[df['customer_key'].isna()]['customer_id'].unique()
if len(missing_customers) > 0:
self.logger.warning(f"Missing customer keys: {missing_customers}")
# In production: create new dimension records
return df
def load_upsert(self, df):
"""Load data using UPSERT strategy"""
self.logger.info(f"Loading {len(df)} records")
# Stage data first
self._load_to_staging(df)
# Merge from staging to target
merge_query = """
MERGE INTO fact_sales AS target
USING staging_sales AS source
ON target.sale_id = source.sale_id
WHEN MATCHED THEN
UPDATE SET
date_key = source.date_key,
customer_key = source.customer_key,
product_key = source.product_key,
store_key = source.store_key,
quantity = source.quantity,
unit_price = source.unit_price,
discount = source.discount,
tax_amount = source.tax_amount,
total_amount = source.total_amount,
cost_amount = source.cost_amount
WHEN NOT MATCHED THEN
INSERT (sale_id, date_key, customer_key, product_key, store_key,
quantity, unit_price, discount, tax_amount,
total_amount, cost_amount)
VALUES (source.sale_id, source.date_key, source.customer_key,
source.product_key, source.store_key, source.quantity,
source.unit_price, source.discount, source.tax_amount,
source.total_amount, source.cost_amount);
"""
cursor = self.target.cursor()
cursor.execute(merge_query)
self.target.commit()
rows_affected = cursor.rowcount
self.logger.info(f"Loaded {rows_affected} records")
# Clean up staging
cursor.execute("TRUNCATE TABLE staging_sales")
self.target.commit()
return rows_affected
def _load_to_staging(self, df):
"""Load data to staging table"""
df.to_sql('staging_sales', self.target, if_exists='replace',
index=False, method='multi', chunksize=1000)
def get_last_run_date(self):
"""Get last successful run timestamp"""
query = """
SELECT MAX(last_run_date) as last_run
FROM etl_run_log
WHERE pipeline_name = 'sales_etl'
AND status = 'SUCCESS'
"""
result = pd.read_sql(query, self.target)
if result['last_run'].iloc[0] is None:
# First run - go back 30 days
return datetime.now() - timedelta(days=30)
return result['last_run'].iloc[0]
def log_run(self, status, records_processed, error_msg=None):
"""Log pipeline execution"""
log_query = """
INSERT INTO etl_run_log
(pipeline_name, run_date, last_run_date, status,
records_processed, error_message)
VALUES (%s, %s, %s, %s, %s, %s)
"""
cursor = self.target.cursor()
cursor.execute(log_query, (
'sales_etl',
datetime.now(),
self.get_last_run_date(),
status,
records_processed,
error_msg
))
self.target.commit()
def run(self):
"""Execute full ETL pipeline"""
try:
# Get last successful run
last_run = self.get_last_run_date()
# Extract
df = self.extract_incremental(last_run)
if len(df) == 0:
self.logger.info("No new records to process")
return
# Transform
df = self.transform(df)
# Load
rows_loaded = self.load_upsert(df)
# Log success
self.log_run('SUCCESS', rows_loaded)
self.logger.info("ETL pipeline completed successfully")
except Exception as e:
self.logger.error(f"ETL pipeline failed: {e}", exc_info=True)
self.log_run('FAILED', 0, str(e))
raise
# Orchestration with Airflow
from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime
def run_sales_etl():
# Connection setup
source_conn = get_source_connection()
target_conn = get_target_connection()
# Run pipeline
etl = SalesETL(source_conn, target_conn)
etl.run()
dag = DAG(
'sales_etl_pipeline',
schedule_interval='0 2 * * *', # Run daily at 2 AM
start_date=datetime(2024, 1, 1),
catchup=False,
tags=['etl', 'sales']
)
run_etl = PythonOperator(
task_id='run_sales_etl',
python_callable=run_sales_etl,
dag=dag
)
ETL Run Log Table:
CREATE TABLE etl_run_log (
run_id SERIAL PRIMARY KEY,
pipeline_name VARCHAR(100) NOT NULL,
run_date TIMESTAMP NOT NULL,
last_run_date TIMESTAMP,
status VARCHAR(20) NOT NULL,
records_processed INT,
error_message TEXT,
duration_seconds INT
);