| name | enterprise-data-engineering-pipeline-ssis-pyspark |
| description | End-to-end ELT pipeline using SSIS, SQL Server, and PySpark for enterprise data warehousing and analytics |
| triggers | ["set up an enterprise ETL pipeline with SSIS","create a star schema data warehouse in SQL Server","build a data engineering pipeline with SSIS and PySpark","implement ETL with SQL Server Integration Services","process enterprise data with PySpark analytics","design a fact and dimension table warehouse","run data quality audits on SQL Server warehouse","scale data processing with PySpark for millions of rows"] |
Enterprise Data Engineering Pipeline (SSIS + PySpark)
Skill by ara.so — Data Skills collection.
Overview
This project provides a complete enterprise data engineering solution that combines:
- SSIS (SQL Server Integration Services) for ETL orchestration
- SQL Server with Star Schema data warehouse design (fact and dimension tables)
- Python (Pandas) for data quality audits and visualization
- PySpark for big data analytics and aggregation
The pipeline ingests raw CSV files (Sales, Products, Customers), transforms them through SSIS, loads into a dimensional model, and performs analytics at scale.
Architecture Components
- Source Layer: Raw CSV files containing transactional and master data
- ETL Layer: SSIS packages handle extraction, transformation, error handling
- Storage Layer: SQL Server Data Warehouse with Star Schema
- Analytics Layer: Python/PySpark scripts for business intelligence
Installation & Setup
Prerequisites
- SQL Server 2019+ (Developer or Enterprise Edition)
- SQL Server Integration Services (SSIS)
- Visual Studio with SQL Server Data Tools (SSDT)
- Python 3.10+
- Java 8+ (for PySpark)
Python Dependencies
pip install pandas sqlalchemy pyodbc pyspark matplotlib
Database Setup
CREATE DATABASE EnterpriseDataWarehouse;
GO
USE EnterpriseDataWarehouse;
GO
CREATE TABLE dim_Customers (
CustomerID INT PRIMARY KEY,
CustomerName NVARCHAR(100),
Email NVARCHAR(100),
Region NVARCHAR(50),
RegistrationDate DATE
);
CREATE TABLE dim_Products (
ProductID INT PRIMARY KEY,
ProductName NVARCHAR(100),
Category NVARCHAR(50),
UnitPrice DECIMAL(10, 2)
);
CREATE TABLE fact_Sales (
SaleID INT PRIMARY KEY,
CustomerID INT FOREIGN KEY REFERENCES dim_Customers(CustomerID),
ProductID INT FOREIGN KEY REFERENCES dim_Products(ProductID),
Quantity INT,
SaleDate DATE,
TotalAmount DECIMAL(10, 2)
);
CREATE VIEW vw_RevenueByProduct AS
SELECT
p.ProductName,
p.Category,
SUM(s.TotalAmount) AS TotalRevenue,
SUM(s.Quantity) AS TotalQuantity
FROM fact_Sales s
INNER dim_Products p s.ProductID p.ProductID
p.ProductName, p.Category;
vw_CustomerLTV
c.CustomerID,
c.CustomerName,
c.Region,
(s.SaleID) TotalPurchases,
(s.TotalAmount) LifetimeValue
dim_Customers c
fact_Sales s c.CustomerID s.CustomerID
c.CustomerID, c.CustomerName, c.Region;
SSIS Package Configuration
Creating the SSIS Project
- Open Visual Studio with SSDT
- Create new Integration Services Project:
EnterpriseETL.sln
- Add Connection Managers:
- Source_FlatFile: Points to CSV directory
- Destination_OLEDB: SQL Server connection string
SSIS Package Flow
- Flat File Source (Customers.csv)
- Data Conversion (handle Unicode, trim strings)
- Derived Column (add audit columns)
- OLEDB Destination (dim_Customers)
- Flat File Source (Products.csv)
- Data Conversion (decimal precision for prices)
- OLEDB Destination (dim_Products)
- Flat File Source (Sales.csv)
- Lookup Transformation (validate CustomerID, ProductID)
- Derived Column (calculate TotalAmount = Quantity * UnitPrice)
- OLEDB Destination (fact_Sales)
Error Handling in SSIS
CREATE TABLE ETL_ErrorLog (
ErrorID INT IDENTITY(1,1) PRIMARY KEY,
PackageName NVARCHAR(100),
TaskName NVARCHAR(100),
ErrorDescription NVARCHAR(MAX),
ErrorDate DATETIME DEFAULT GETDATE()
);
Python Analytics
Data Quality Audit Script
import pandas as pd
import pyodbc
from sqlalchemy import create_engine
import matplotlib.pyplot as plt
def get_connection():
conn_str = (
"mssql+pyodbc:///?odbc_connect="
"DRIVER={ODBC Driver 17 for SQL Server};"
f"SERVER={os.getenv('SQL_SERVER')};"
f"DATABASE={os.getenv('SQL_DATABASE')};"
"Trusted_Connection=yes;"
)
return create_engine(conn_str)
def run_audit():
engine = get_connection()
query_nulls = """
SELECT
'dim_Customers' AS TableName,
SUM(CASE WHEN CustomerName IS NULL THEN 1 ELSE 0 END) AS NullCustomerName,
SUM(CASE WHEN Email IS NULL THEN 1 ELSE 0 END) AS NullEmail
FROM dim_Customers
UNION ALL
SELECT
'fact_Sales',
SUM(CASE WHEN CustomerID IS NULL THEN 1 ELSE 0 END),
SUM(CASE WHEN ProductID IS NULL THEN 1 ELSE 0 END)
FROM fact_Sales
"""
df_nulls = pd.read_sql(query_nulls, engine)
print("Null Value Audit:")
print(df_nulls)
query_orphans = """
SELECT COUNT(*) AS OrphanedSales
FROM fact_Sales s
WHERE NOT EXISTS (SELECT 1 FROM dim_Customers c WHERE c.CustomerID = s.CustomerID)
OR NOT EXISTS (SELECT 1 FROM dim_Products p WHERE p.ProductID = s.ProductID)
"""
df_orphans = pd.read_sql(query_orphans, engine)
print("\nOrphaned Records:")
print(df_orphans)
query_revenue =
df_revenue = pd.read_sql(query_revenue, engine)
plt.figure(figsize=(, ))
plt.bar(df_revenue[][:], df_revenue[][:])
plt.xlabel()
plt.ylabel()
plt.title()
plt.xticks(rotation=, ha=)
plt.tight_layout()
plt.savefig()
()
__name__ == :
run_audit()
Customer Segmentation Analysis
import pandas as pd
from sqlalchemy import create_engine
import os
def segment_customers():
engine = create_engine(
f"mssql+pyodbc:///?odbc_connect="
f"DRIVER={{ODBC Driver 17 for SQL Server}};"
f"SERVER={os.getenv('SQL_SERVER')};"
f"DATABASE={os.getenv('SQL_DATABASE')};"
f"Trusted_Connection=yes;"
)
df = pd.read_sql("SELECT * FROM vw_CustomerLTV", engine)
df['Segment'] = pd.cut(
df['LifetimeValue'],
bins=[0, 1000, 5000, float('inf')],
labels=['Bronze', 'Silver', 'Gold']
)
segment_summary = df.groupby('Segment').agg({
'CustomerID': 'count',
'LifetimeValue': 'sum',
'TotalPurchases': 'mean'
}).reset_index()
print(segment_summary)
return segment_summary
PySpark Big Data Processing
High-Volume Sales Aggregation
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, sum as _sum, count, avg, year, month
import os
spark = SparkSession.builder \
.appName("EnterpriseSalesAnalytics") \
.config("spark.jars", "mssql-jdbc-9.4.0.jre8.jar") \
.getOrCreate()
jdbc_url = f"jdbc:sqlserver://{os.getenv('SQL_SERVER')}:1433;databaseName={os.getenv('SQL_DATABASE')}"
connection_properties = {
"user": os.getenv('SQL_USER'),
"password": os.getenv('SQL_PASSWORD'),
"driver": "com.microsoft.sqlserver.jdbc.SQLServerDriver"
}
df_sales = spark.read.jdbc(
url=jdbc_url,
table="fact_Sales",
properties=connection_properties
)
df_customers = spark.read.jdbc(
url=jdbc_url,
table="dim_Customers",
properties=connection_properties
)
df_products = spark.read.jdbc(
url=jdbc_url,
table="dim_Products",
properties=connection_properties
)
df_combined = df_sales \
.join(df_customers, "CustomerID") \
.join(df_products, "ProductID")
df_monthly = df_combined \
.withColumn("Year", year(col("SaleDate"))) \
.withColumn("Month", month(col("SaleDate"))) \
.groupBy("Year", "Month", "Region") \
.agg(
_().alias(),
count().alias(),
avg().alias()
) \
.orderBy(, , )
df_monthly.show()
df_monthly.write.jdbc(
url=jdbc_url,
table=,
mode=,
properties=connection_properties
)
df_category = df_combined \
.groupBy() \
.agg(
_().alias(),
_().alias(),
count().distinct().alias()
) \
.orderBy(col().desc())
df_category.show()
spark.stop()
Parallel Processing for Large Datasets
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, when
spark = SparkSession.builder \
.appName("BatchDataProcessing") \
.config("spark.executor.memory", "4g") \
.config("spark.executor.cores", "4") \
.getOrCreate()
df_raw = spark.read.csv(
"hdfs://data/sales/*.csv",
header=True,
inferSchema=True
)
df_cleaned = df_raw \
.filter(col("TotalAmount").isNotNull()) \
.filter(col("Quantity") > 0) \
.withColumn(
"IsHighValue",
when(col("TotalAmount") > 1000, "Yes").otherwise("No")
) \
.dropDuplicates(["SaleID"])
df_cleaned.write \
.partitionBy("SaleDate") \
.mode("overwrite") \
.parquet("hdfs://data/processed/sales_cleaned")
print(f"Processed {df_cleaned.count()} records")
Common Patterns
Incremental ETL (Load Only New Records)
CREATE TABLE stg_Sales (
SaleID INT,
CustomerID INT,
ProductID INT,
Quantity INT,
SaleDate DATE,
TotalAmount DECIMAL(10, 2),
LoadDate DATETIME DEFAULT GETDATE()
);
MERGE INTO fact_Sales AS target
USING stg_Sales AS source
ON target.SaleID = source.SaleID
WHEN MATCHED THEN
UPDATE SET
Quantity = source.Quantity,
TotalAmount = source.TotalAmount
WHEN NOT MATCHED THEN
INSERT (SaleID, CustomerID, ProductID, Quantity, SaleDate, TotalAmount)
VALUES (source.SaleID, source.CustomerID, source.ProductID,
source.Quantity, source.SaleDate, source.TotalAmount);
Automated Data Refresh
import subprocess
import os
from datetime import datetime
def run_ssis_package():
"""Execute SSIS package via dtexec"""
package_path = r"C:\SSIS\EnterpriseETL\EnterpriseETL\Package.dtsx"
cmd = [
"dtexec",
"/FILE", package_path,
"/REPORTING", "E"
]
result = subprocess.run(cmd, capture_output=True, text=True)
log_file = f"etl_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
with open(log_file, 'w') as f:
f.write(result.stdout)
f.write(result.stderr)
return result.returncode == 0
def run_analytics():
"""Execute Python analytics after ETL"""
subprocess.run(["python", "project_audit.py"])
subprocess.run(["python", "pyspark_analytics.py"])
if __name__ == "__main__":
if run_ssis_package():
print("ETL completed successfully")
run_analytics()
else:
print("ETL failed - check logs")
Troubleshooting
SSIS Connection Issues
Error: "Cannot acquire connection to SQL Server"
Solution:
1. Verify SQL Server service is running
2. Check Windows Authentication vs SQL Authentication
3. Update connection string in Connection Manager
4. Enable TCP/IP protocol in SQL Server Configuration Manager
Unicode/Encoding Errors
Error: "Cannot convert between unicode and non-unicode string data types"
Solution in SSIS:
1. Add Data Conversion task
2. Convert DT_STR to DT_WSTR for Unicode columns
3. Set CodePage to 1252 (Windows Latin 1) in Flat File Connection
PySpark JDBC Driver Not Found
wget https://github.com/microsoft/mssql-jdbc/releases/download/v9.4.0/mssql-jdbc-9.4.0.jre8.jar
spark = SparkSession.builder \
.config("spark.jars", "/path/to/mssql-jdbc-9.4.0.jre8.jar") \
.getOrCreate()
Performance Optimization
from pyspark.sql.functions import broadcast
df_result = df_sales.join(
broadcast(df_products),
"ProductID"
)
df_sales.cache()
df_sales.count()
Environment Variables
SQL_SERVER=localhost
SQL_DATABASE=EnterpriseDataWarehouse
SQL_USER=your_username
SQL_PASSWORD=your_password
SPARK_HOME=/path/to/spark
JAVA_HOME=/path/to/java
Running the Complete Pipeline
sqlcmd -S localhost -i 01_Schema_Setup.sql
dtexec /FILE "EnterpriseETL\Package.dtsx"
python project_audit.py
spark-submit --jars mssql-jdbc-9.4.0.jre8.jar pyspark_analytics.py
This enterprise pipeline provides a complete solution for data warehousing, ETL automation, and big data analytics using industry-standard Microsoft and Apache technologies.