| FullName | [FirstName] + " " + [LastName] | DT_WSTR(100) | | FormattedPhone | REPLACE([Phone],"-","") | DT_WSTR(20) | | LoadDate | GETDATE() | DT_DBTIMESTAMP |
Conditional Split Transformation:
| Output Name | Condition |
|---|---|
| ValidRecords | !ISNULL([Email]) && LEN([Email]) > 5 |
| InvalidRecords | (Default Output) |
Step 5: Error Handling
<!-- Configure error output on source -->
<OleDbSource ErrorRowDisposition="RedirectRow">
<!-- Errors redirected to error log file -->
</OleDbSource>
Slowly Changing Dimensions (SCD)
SCD Type 1: Overwrite (No History)
-- Merge statement for Type 1
MERGE dbo.DimCustomer AS target
USING dbo.StagingCustomer AS source
ON target.CustomerID = source.CustomerID
WHEN MATCHED THEN
```sql
UPDATE SET
target.CustomerName = source.CustomerName,
target.Email = source.Email,
target.ModifiedDate = GETDATE()
WHEN NOT MATCHED BY TARGET THEN
INSERT (CustomerID, CustomerName, Email, CreatedDate)
VALUES (source.CustomerID, source.CustomerName, source.Email, GETDATE());
**SCD Type 2: Preserve History (Versioning)**
```sql
-- Add version columns to dimension
ALTER TABLE dbo.DimCustomer ADD
```text
EffectiveDate DATETIME NOT NULL DEFAULT('1900-01-01'),
ExpirationDate DATETIME NULL,
IsCurrent BIT NOT NULL DEFAULT(1);
-- SCD Type 2 merge logic -- 1. Expire current records where values changed UPDATE target SET
target.IsCurrent = 0,
target.ExpirationDate = GETDATE()```
FROM dbo.DimCustomer target
INNER JOIN dbo.StagingCustomer source ON target.CustomerID = source.CustomerID
WHERE
```text
target.IsCurrent = 1
AND (target.CustomerName <> source.CustomerName OR target.Email <> source.Email);
-- 2. Insert new versions for changed records INSERT INTO dbo.DimCustomer (CustomerID, CustomerName, Email, EffectiveDate, IsCurrent) SELECT
source.CustomerID,
source.CustomerName,
source.Email,
GETDATE(),
1```
FROM dbo.StagingCustomer source
INNER JOIN dbo.DimCustomer target ON source.CustomerID = target.CustomerID
WHERE
```text
target.IsCurrent = 0
AND target.ExpirationDate = GETDATE();
-- 3. Insert new records INSERT INTO dbo.DimCustomer (CustomerID, CustomerName, Email, EffectiveDate, IsCurrent) SELECT
CustomerID,
CustomerName,
Email,
GETDATE(),
1```
FROM dbo.StagingCustomer
WHERE CustomerID NOT IN (SELECT CustomerID FROM dbo.DimCustomer);
> **Architecture Overview:** **SSIS Slowly Changing Dimension Wizard:**
-- Track last successful load timestamp
CREATE TABLE dbo.ETLControl (
```text
PackageName NVARCHAR(100) PRIMARY KEY,
LastLoadDate DATETIME,
LastLoadStatus NVARCHAR(20)```
);
-- SSIS Package variable: @LastLoadDate
-- Set from ETLControl table in Execute SQL Task
-- Incremental extract query
SELECT
```text
CustomerID,
CustomerName,
Email,
ModifiedDate```
FROM dbo.Customers
WHERE ModifiedDate > ? -- Parameter from @LastLoadDate variable
-- Update ETLControl after successful load
UPDATE dbo.ETLControl
SET
```text
LastLoadDate = GETDATE(),
LastLoadStatus = 'Success'```
WHERE PackageName = 'CustomerETL';
Package Parameters and Configurations
Project Parameters:
- Right-click project → Parameters
- Add: ServerName, DatabaseName, EmailRecipient
Use Parameter in Connection Manager:
- Right-click Connection Manager → Parameterize
- Property: ConnectionString
- Expression:
"Data Source=" + @[$Project::ServerName] + ";Initial Catalog=" + @[$Project::DatabaseName]
Environment Variables (SSISDB Catalog):
-- Create environment
EXEC catalog.create_environment
```text
@folder_name = 'ETL',
@environment_name = 'Production';
-- Add environment variables EXEC catalog.create_environment_variable
@folder_name = 'ETL',
@environment_name = 'Production',
@variable_name = 'ServerName',
@data_type = 'String',
@value = 'PROD-SQL01';
-- Map to project parameter EXEC catalog.create_environment_reference
@folder_name = 'ETL',
@project_name = 'CustomerETL',
@environment_name = 'Production',
@reference_id = @reference_id OUTPUT;
EXEC catalog.set_object_parameter_value
@folder_name = 'ETL',
@project_name = 'CustomerETL',
@parameter_name = 'ServerName',
@parameter_value = 'ServerName',
@object_type = 20,
@value_type = 'R'; -- R = Reference
## SSRS - Reporting Services
### Creating a Report
**SQL Server Data Tools → New Report:**
1. **Data Source:**
```xml
<DataSource Name="AdventureWorks">
<ConnectionString>
```text
Data Source=localhost;Initial Catalog=AdventureWorks```
</ConnectionString>
<UseSharedDataSource>true</UseSharedDataSource>
</DataSource>
- Dataset:
SELECT
```text
o.OrderID,
c.CustomerName,
o.OrderDate,
o.TotalAmount,
o.Status```
FROM dbo.Orders o
INNER JOIN dbo.Customers c ON o.CustomerID = c.CustomerID
WHERE
```text
(@StartDate IS NULL OR o.OrderDate >= @StartDate)
AND (@EndDate IS NULL OR o.OrderDate <= @EndDate)
AND (@Status IS NULL OR o.Status = @Status)```
ORDER BY o.OrderDate DESC;
- Report Parameters:
| Parameter | Data Type | Default Value | Allow Blank | Allow Null |
|---|---|---|---|---|
| StartDate | DateTime | =DateAdd("m", -1, Today()) | ✓ | ✓ |
| EndDate | DateTime | =Today() | ✓ | ✓ |
| Status | String | "All" | ✓ | ✓ |
Status Parameter - Available Values:
- Dataset query:
SELECT DISTINCT Status FROM dbo.Orders
UNION ALL
SELECT 'All' AS Status;
- Report Layout:
Tablix (Table) Control:
Diagram: See the official Microsoft documentation for architecture details.
Expressions:
- Format currency:
=Format(Fields!TotalAmount.Value, "C") - Conditional formatting:
=IIF(Fields!Status.Value = "Cancelled", "Red", "Black")
Matrix Report (Pivot Table)
-- Dataset for matrix
SELECT
```text
YEAR(OrderDate) AS OrderYear,
MONTH(OrderDate) AS OrderMonth,
Status,
SUM(TotalAmount) AS Revenue```
FROM dbo.Orders
GROUP BY YEAR(OrderDate), MONTH(OrderDate), Status;
Matrix Layout:
Architecture Overview: Pending Completed Cancelled Row Total
Chart Reports
Add Chart Control:
- Toolbox → Chart → Drag to report
- Chart Type: Column, Bar, Line, Pie, Area
Configure Chart:
Category Groups: =Fields!OrderMonth.Value
Series Groups: =Fields!Status.Value
Values: =Sum(Fields!TotalAmount.Value)
Subscriptions
SQL Server Reporting Services Portal:
- Navigate to report
- Subscribe → Create subscription
Email Subscription:
- To: sales@contoso.com
- Subject: Monthly Sales Report - @ReportExecutionTime
- Render Format: PDF
- Schedule: Monthly (1st day, 8:00 AM)
Data-Driven Subscription (Enterprise Edition):
-- Create subscriber list
CREATE TABLE dbo.ReportSubscribers (
```text
SubscriberID INT PRIMARY KEY IDENTITY,
EmailAddress NVARCHAR(100),
CustomerID INT,
FileFormat NVARCHAR(20)```
);
-- Query for data-driven subscription
SELECT
```text
EmailAddress AS TO,
'Customer Report' AS Subject,
FileFormat,
CustomerID -- Passed to report parameter```
FROM dbo.ReportSubscribers;
SSAS - Analysis Services
Creating a Tabular Model
SQL Server Data Tools → New Project → Analysis Services Tabular Project
1. Import Data:
- Model → Import from Data Source
- Choose SQL Server
- Select tables: FactSales, DimCustomer, DimProduct, DimDate
2. Create Relationships:
Architecture Overview: FactSales[CustomerKey] → DimCustomer[CustomerKey]
3. Create Measures (DAX):
-- Total Sales
TotalSales := SUM(FactSales[SalesAmount])
-- Total Quantity
TotalQuantity := SUM(FactSales[Quantity])
-- Average Order Value
AvgOrderValue := DIVIDE([TotalSales], DISTINCTCOUNT(FactSales[OrderID]))
-- Sales YTD
SalesYTD := TOTALYTD([TotalSales], DimDate[Date])
-- Sales Previous Year
SalesPY := CALCULATE([TotalSales], SAMEPERIODLASTYEAR(DimDate[Date]))
-- Sales Growth %
SalesGrowth% := DIVIDE([TotalSales] - [SalesPY], [SalesPY], 0)
-- Top 10 Products
Top10ProductSales :=
CALCULATE(
```text
[TotalSales],
TOPN(10, ALL(DimProduct[ProductName]), [TotalSales], DESC)```
)
> **Architecture Overview:** **4. Create Hierarchies:**
-- Full Name
DimCustomer[FullName] = DimCustomer[FirstName] & " " & DimCustomer[LastName]
-- Sales Category
FactSales[SalesCategory] =
```text
SWITCH(
TRUE(),
FactSales[SalesAmount] >= 1000, "High",
FactSales[SalesAmount] >= 500, "Medium",
"Low"
)
### MDX Queries (Multidimensional Model)
```mdx
-- Basic MDX query
SELECT
```text
[Measures].[Total Sales] ON COLUMNS,
[DimDate].[Calendar].[Month].MEMBERS ON ROWS```
FROM [SalesCube];
-- Filter by year
SELECT
```text
[Measures].[Total Sales] ON COLUMNS,
[DimProduct].[Category].[Category].MEMBERS ON ROWS```
FROM [SalesCube]
WHERE [DimDate].[Calendar].[Year].&[2025];
-- Top 10 products
SELECT
```text
[Measures].[Total Sales] ON COLUMNS,
TOPCOUNT([DimProduct].[Product].[Product].MEMBERS, 10, [Measures].[Total Sales]) ON ROWS```
FROM [SalesCube];
-- Year-over-year growth
WITH MEMBER [Measures].[Sales Growth] AS
```text
([Measures].[Total Sales] -
([Measures].[Total Sales], ParallelPeriod([DimDate].[Calendar].[Year], 1)))
/ ([Measures].[Total Sales], ParallelPeriod([DimDate].[Calendar].[Year], 1))
, FORMAT_STRING = "Percent"```
SELECT
```text
{[Measures].[Total Sales], [Measures].[Sales Growth]} ON COLUMNS,
[DimDate].[Calendar].[Year].MEMBERS ON ROWS```
FROM [SalesCube];
Connecting to SSAS from Excel
Excel → Data → Get Data → From Database → From Analysis Services
- Server: localhost\TABULAR
- Database: SalesModel
- Connect
PivotTable:
- Rows: DimProduct[Category]
- Columns: DimDate[Year]
- Values: TotalSales
Excel Formulas with SSAS:
=CUBEVALUE("SalesModel", "[Measures].[TotalSales]", "[DimDate].[Year].&[2025]")
=CUBEMEMBER("SalesModel", "[DimProduct].[Category].&[Electronics]")
Deployment
Deploy SSIS Package to SSISDB
# PowerShell deployment script
$SSISNamespace = "Microsoft.SqlServer.Management.IntegrationServices"
[Reflection.Assembly]::LoadWithPartialName($SSISNamespace) | Out-Null
$sqlConnectionString = "Data Source=localhost;Initial Catalog=master;Integrated Security=SSPI;"
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection $sqlConnectionString
$integrationServices = New-Object $SSISNamespace.IntegrationServices $sqlConnection
## Create catalog if not exists
if (!$integrationServices.Catalogs["SSISDB"]) {
```powershell
$catalog = New-Object $SSISNamespace.Catalog ($integrationServices, "SSISDB", "P@ssw0rd123!")
$catalog.Create()```
}
$catalog = $integrationServices.Catalogs["SSISDB"]
## Create folder
$folder = New-Object $SSISNamespace.CatalogFolder ($catalog, "ETL", "ETL Projects")
$folder.Create()
## Deploy project
[byte[]] $projectFile = [System.IO.File]::ReadAllBytes("C:\Projects\CustomerETL\bin\Development\CustomerETL.ispac")
$folder.DeployProject("CustomerETL", $projectFile)
Write-Host "Project deployed successfully"
Execute SSIS Package
Figure: SSIS package designer – data flow, transformations, and connections.
-- Execute package from SSISDB
DECLARE @execution_id BIGINT;
EXEC catalog.create_execution
```text
@folder_name = 'ETL',
@project_name = 'CustomerETL',
@package_name = 'LoadCustomers.dtsx',
@execution_id = @execution_id OUTPUT;
-- Set parameter values EXEC catalog.set_execution_parameter_value
@execution_id,
@object_type = 50, -- Package
@parameter_name = N'ServerName',
@parameter_value = 'PROD-SQL01';
-- Start execution EXEC catalog.start_execution @execution_id;
-- Check execution status SELECT
execution_id,
folder_name,
project_name,
package_name,
status,
start_time,
end_time```
FROM catalog.executions
WHERE execution_id = @execution_id;
SQL Server Agent Job for SSIS
-- Create job
EXEC msdb.dbo.sp_add_job
```text
@job_name = 'Daily Customer ETL',
@enabled = 1;
-- Add job step EXEC msdb.dbo.sp_add_jobstep
@job_name = 'Daily Customer ETL',
@step_name = 'Execute SSIS Package',
@subsystem = 'SSIS',
@command = '/ISSERVER "\"\SSISDB\ETL\CustomerETL\LoadCustomers.dtsx\"" /SERVER "\"localhost\"" /Par "\"$ServerOption::LOGGING_LEVEL(Int16)\"";1 /Par "\"$ServerOption::SYNCHRONIZED(Boolean)\"";True /CALLERINFO SQLAGENT /REPORTING E',
@retry_attempts = 3,
@retry_interval = 5;
-- Add schedule (daily at 2 AM) EXEC msdb.dbo.sp_add_schedule
@schedule_name = 'Daily at 2 AM',
@freq_type = 4, -- Daily
@freq_interval = 1,
@active_start_time = 020000;
EXEC msdb.dbo.sp_attach_schedule
@job_name = 'Daily Customer ETL',
@schedule_name = 'Daily at 2 AM';
-- Add notification EXEC msdb.dbo.sp_add_notification
@alert_name = 'Daily Customer ETL',
@operator_name = 'DBA',
@notification_method = 1; -- Email
## Architecture Decision and Tradeoffs
When designing data management solutions with SQL Server, consider these key architectural trade-offs:
| Approach | Best For | Tradeoff |
|----------|----------|----------|
| Managed / platform service | Rapid delivery, reduced ops burden | Less customisation, potential vendor lock-in |
| Custom / self-hosted | Full control, advanced tuning | Higher operational overhead and cost |
> **Recommendation:** Start with the managed approach for most workloads and move to custom only when specific requirements demand it.
## Validation and Versioning
- Last validated: April 2026
- Validate examples against your tenant, region, and SKU constraints before production rollout.
- Keep module, CLI, and SDK versions pinned in automation pipelines and review quarterly.
## Security and Governance Considerations
- Apply least-privilege access using RBAC roles and just-in-time elevation for admin tasks.
- Store secrets in managed secret stores and avoid embedding credentials in scripts or source files.
- Enable audit logging, data protection policies, and periodic access reviews for regulated workloads.
## Cost and Performance Notes
- Define budgets and alerts, then monitor usage and cost trends continuously after go-live.
- Baseline performance with synthetic and real-user checks before and after major changes.
- Scale resources with measured thresholds and revisit sizing after usage pattern changes.
## Official Microsoft References
- https://learn.microsoft.com/sql/
- https://learn.microsoft.com/azure/azure-sql/
- https://learn.microsoft.com/fabric/database/
## Public Examples from Official Sources
- These examples are sourced from official public Microsoft documentation and sample repositories.
- Documentation examples: https://learn.microsoft.com/sql/
- Sample repositories: https://github.com/microsoft/sql-server-samples
- Prefer adapting these examples to your tenant, subscriptions, and governance requirements before production use.
## Key Takeaways
- SSIS provides robust ETL with visual designer and transformations
- Use Lookup transformation for reference data validation
- Implement SCD Type 2 for historical dimension tracking
- Design incremental loads with watermark timestamps
- SSRS supports parameterized reports with subscriptions
- Matrix reports enable pivot table-style analysis
- SSAS Tabular uses DAX for powerful business logic
- Deploy SSIS packages to SSISDB catalog for management
- Schedule ETL jobs with SQL Server Agent
- Monitor executions with catalog views
## Next Steps
- Build first SSIS package with error handling
- Create parameterized SSRS report with drill-down
- Design SSAS tabular model with DAX measures
- Automate deployments with PowerShell
- Implement logging and auditing for ETL processes
## Additional Resources
- [SSIS Documentation](https://learn.microsoft.com/sql/integration-services/sql-server-integration-services)
- [SSRS Documentation](https://learn.microsoft.com/sql/reporting-services/create-deploy-and-manage-mobile-and-paginated-reports)
- [SSAS Tabular](https://learn.microsoft.com/analysis-services/tabular-models/tabular-models-ssas)
- [DAX Reference](https://learn.microsoft.com/dax/dax-function-reference)
---
*Extract. Transform. Load. Analyze. Decide.*
Discussion