SSIS / ETL Best Practices
This skill helps optimize SSIS packages and ETL processes for SQL Server environments.
When to Use
- Reviewing SSIS package designs for performance
- Optimizing Data Flow throughput
- Choosing Lookup Transform cache modes
- Implementing incremental load strategies
- Setting up error handling and logging
- Converting row-by-row ETL to set-based operations
Key Decision Points
Lookup Cache Strategy
| Mode |
When to Use |
Performance |
| Full Cache |
Reference table fits in memory (< 25%) |
Best — preloaded at start |
| Partial Cache |
Repeated lookups, large reference table |
Good — LRU cache |
| No Cache |
Avoid when possible |
Worst — DB query per row |
Load Strategy
| Pattern |
When to Use |
| Watermark |
Source has reliable modified_date column |
| CDC |
SQL Server Change Data Capture enabled |
| Hash comparison |
No reliable timestamp, need change detection |
| Full reload |
Small tables, or when incremental is impractical |
Destination Configuration
| Setting |
Recommended Value |
| Table Lock |
ON |
| Check Constraints |
OFF (validate before load) |
| Rows per Batch |
10,000–100,000 |
| Max Insert Commit Size |
0 (all at once) or batch size |
Detailed Patterns
1. Data Flow Optimization
Buffer Sizing:
- DefaultBufferMaxRows: Start with 10,000, tune based on row width
- DefaultBufferSize: Increase to 10 MB (10485760) for wide rows
- BLOBTempStoragePath: Point to fast SSD for LOB data spill
Pipeline Design:
- Minimize transformations between source and destination
- Remove unused columns early in the pipeline (reduces buffer memory)
- Avoid synchronous blocking transforms (Sort, Aggregate) on large datasets
- Use async transforms where possible (Union All, Merge)
Parallelism:
- MaxConcurrentExecutables: Set to server CPU count for CPU-bound workloads
- EngineThreads: Match to MaxConcurrentExecutables
- Use Balanced Data Distributor for multi-threaded destinations
2. Lookup Transform Strategies
| Mode |
When to Use |
Memory |
Performance |
| Full Cache |
Reference table < 25% available memory |
High |
Best (preloaded) |
| Partial Cache |
Repeated lookups, large reference table |
Medium |
Good (LRU cache) |
| No Cache |
Large reference table, few lookups |
None |
Worst (query per row) |
Full Cache (Recommended Default):
- Cache entire reference table at pipeline start
- Best for: dimension tables, code lookups, reference data
- Memory: Holds entire dataset in memory
- Tip: Use SQL query (not table) to limit columns loaded
Partial Cache:
- Cache recently used values (LRU eviction)
- Best for: Large reference tables with skewed access patterns
- Configure: CacheType = Partial, MaxMemoryUsage = 25-50%
- Monitor: Cache hit ratio should be > 80%
No Cache (Avoid When Possible):
- Queries database for EVERY single row
- Only justified for: very large dim tables with random access
- Impact: N queries for N rows (essentially a nested loop join)
- Better alternative: Stage + SQL JOIN
3. Destination Configuration
OLE DB Destination — Fast Load Options:
- Table Lock: ON (minimizes lock overhead)
- Check Constraints: OFF (validate data before loading)
- Rows per Batch: 10,000–100,000
- Maximum Insert Commit Size: 0 (commit all at once) or batch size
- Keep Identity: As needed
- Keep Nulls: As needed
SQL Server Destination (Same-Server Only):
- Uses shared memory (fastest option when available)
- Only works on same server as SSIS runtime
- Enable: Use Bulk Insert, Table Lock, Fire Triggers OFF
Batch Size Guidelines:
| Row Width |
Recommended Batch Size |
| Narrow (< 100 bytes) |
50,000 - 100,000 |
| Medium (100-500 bytes) |
10,000 - 50,000 |
| Wide (> 500 bytes) |
5,000 - 10,000 |
| LOB columns |
1,000 - 5,000 |
4. Incremental Load Patterns
Pattern A: Watermark Column
-- Use a high-watermark date column
-- Source query:
SELECT * FROM SourceTable
WHERE modified_date > @LastLoadDate;
-- After successful load, update watermark:
UPDATE ETL_Config SET last_load_date = GETUTCDATE()
WHERE table_name = 'SourceTable';
Pattern B: Change Data Capture (CDC)
-- Enable CDC on source table
EXEC sys.sp_cdc_enable_table
@source_schema = 'dbo',
@source_name = 'Orders',
@role_name = NULL;
-- Query changes since last LSN
SELECT * FROM cdc.fn_cdc_get_all_changes_dbo_Orders(@from_lsn, @to_lsn, 'all');
Pattern C: Hash Comparison
-- Compute hash of source row, compare to existing hash
SELECT *,
HASHBYTES('SHA2_256',
CONCAT(col1, '|', col2, '|', col3)
) AS row_hash
FROM SourceTable;
5. Error Handling
Row-Level Error Routing:
- Configure each component's Error Output
- Route error rows to error table (not fail package)
- Capture: ErrorCode, ErrorColumn, source data
- Threshold: Fail package if error rows > N%
Package-Level Event Handlers:
- OnError: Log full error details, send alert
- OnWarning: Log warnings for review
- OnPostExecute: Log execution statistics per component
- OnTaskFailed: Custom retry logic or escalation
Transaction Scope:
- Use TransactionOption = Required for atomic packages
- Supported on Control Flow (not Data Flow internals)
- Use checkpoint files for restartability
6. Logging Standards
Required Log Events:
| Event |
Information Captured |
| OnPreExecute |
Component name, start time |
| OnPostExecute |
Component name, end time, rows processed |
| OnError |
Error code, description, source component |
| OnWarning |
Warning details |
| Package start/end |
Duration, status, initiation method |
Performance Counters to Monitor:
- Rows read / written per second
- Buffer spools to disk
- BLOB bytes read/written
- Seconds spent in transforms vs destinations
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: ssis-best-practices3description: Optimize SSIS (SQL Server Integration Services) and ETL packages including Data Flow buffer tuning, Lookup Transform cache strategies (Full Cache vs Partial vs No Cache), OLE DB Destination Fast Load configuration, incremental load patterns (watermark, CDC), error handling with event handlers, and package logging standards. Use when this capability is needed.4---56# SSIS / ETL Best Practices78This skill helps optimize SSIS packages and ETL processes for SQL Server environments.910## When to Use1112- Reviewing SSIS package designs for performance13- Optimizing Data Flow throughput14- Choosing Lookup Transform cache modes15- Implementing incremental load strategies16- Setting up error handling and logging17- Converting row-by-row ETL to set-based operations1819## Key Decision Points2021### Lookup Cache Strategy2223| Mode | When to Use | Performance |24|------|-------------|-------------|25| **Full Cache** | Reference table fits in memory (< 25%) | Best — preloaded at start |26| **Partial Cache** | Repeated lookups, large reference table | Good — LRU cache |27| **No Cache** | Avoid when possible | Worst — DB query per row |2829### Load Strategy3031| Pattern | When to Use |32|---------|-------------|33| **Watermark** | Source has reliable `modified_date` column |34| **CDC** | SQL Server Change Data Capture enabled |35| **Hash comparison** | No reliable timestamp, need change detection |36| **Full reload** | Small tables, or when incremental is impractical |3738### Destination Configuration3940| Setting | Recommended Value |41|---------|-------------------|42| Table Lock | ON |43| Check Constraints | OFF (validate before load) |44| Rows per Batch | 10,000–100,000 |45| Max Insert Commit Size | 0 (all at once) or batch size |4647## Detailed Patterns4849### 1. Data Flow Optimization5051**Buffer Sizing:**52- **DefaultBufferMaxRows**: Start with 10,000, tune based on row width53- **DefaultBufferSize**: Increase to 10 MB (10485760) for wide rows54- **BLOBTempStoragePath**: Point to fast SSD for LOB data spill5556**Pipeline Design:**57- Minimize transformations between source and destination58- Remove unused columns early in the pipeline (reduces buffer memory)59- Avoid synchronous blocking transforms (Sort, Aggregate) on large datasets60- Use async transforms where possible (Union All, Merge)6162**Parallelism:**63- **MaxConcurrentExecutables**: Set to server CPU count for CPU-bound workloads64- **EngineThreads**: Match to MaxConcurrentExecutables65- Use Balanced Data Distributor for multi-threaded destinations6667### 2. Lookup Transform Strategies6869| Mode | When to Use | Memory | Performance |70|------|-------------|--------|-------------|71| **Full Cache** | Reference table < 25% available memory | High | Best (preloaded) |72| **Partial Cache** | Repeated lookups, large reference table | Medium | Good (LRU cache) |73| **No Cache** | Large reference table, few lookups | None | Worst (query per row) |7475**Full Cache (Recommended Default):**76- Cache entire reference table at pipeline start77- Best for: dimension tables, code lookups, reference data78- Memory: Holds entire dataset in memory79- Tip: Use SQL query (not table) to limit columns loaded8081**Partial Cache:**82- Cache recently used values (LRU eviction)83- Best for: Large reference tables with skewed access patterns84- Configure: CacheType = Partial, MaxMemoryUsage = 25-50%85- Monitor: Cache hit ratio should be > 80%8687**No Cache (Avoid When Possible):**88- Queries database for EVERY single row89- Only justified for: very large dim tables with random access90- Impact: N queries for N rows (essentially a nested loop join)91- Better alternative: Stage + SQL JOIN9293### 3. Destination Configuration9495**OLE DB Destination — Fast Load Options:**96- Table Lock: ON (minimizes lock overhead)97- Check Constraints: OFF (validate data before loading)98- Rows per Batch: 10,000–100,00099- Maximum Insert Commit Size: 0 (commit all at once) or batch size100- Keep Identity: As needed101- Keep Nulls: As needed102103**SQL Server Destination (Same-Server Only):**104- Uses shared memory (fastest option when available)105- Only works on same server as SSIS runtime106- Enable: Use Bulk Insert, Table Lock, Fire Triggers OFF107108**Batch Size Guidelines:**109110| Row Width | Recommended Batch Size |111|-----------|----------------------|112| Narrow (< 100 bytes) | 50,000 - 100,000 |113| Medium (100-500 bytes) | 10,000 - 50,000 |114| Wide (> 500 bytes) | 5,000 - 10,000 |115| LOB columns | 1,000 - 5,000 |116117### 4. Incremental Load Patterns118119**Pattern A: Watermark Column**120```sql121-- Use a high-watermark date column122-- Source query:123SELECT * FROM SourceTable124WHERE modified_date > @LastLoadDate;125126-- After successful load, update watermark:127UPDATE ETL_Config SET last_load_date = GETUTCDATE()128WHERE table_name = 'SourceTable';129```130131**Pattern B: Change Data Capture (CDC)**132```sql133-- Enable CDC on source table134EXEC sys.sp_cdc_enable_table135 @source_schema = 'dbo',136 @source_name = 'Orders',137 @role_name = NULL;138139-- Query changes since last LSN140SELECT * FROM cdc.fn_cdc_get_all_changes_dbo_Orders(@from_lsn, @to_lsn, 'all');141```142143**Pattern C: Hash Comparison**144```sql145-- Compute hash of source row, compare to existing hash146SELECT *,147 HASHBYTES('SHA2_256', 148 CONCAT(col1, '|', col2, '|', col3)149 ) AS row_hash150FROM SourceTable;151```152153### 5. Error Handling154155**Row-Level Error Routing:**156- Configure each component's Error Output157- Route error rows to error table (not fail package)158- Capture: ErrorCode, ErrorColumn, source data159- Threshold: Fail package if error rows > N%160161**Package-Level Event Handlers:**162- OnError: Log full error details, send alert163- OnWarning: Log warnings for review164- OnPostExecute: Log execution statistics per component165- OnTaskFailed: Custom retry logic or escalation166167**Transaction Scope:**168- Use TransactionOption = Required for atomic packages169- Supported on Control Flow (not Data Flow internals)170- Use checkpoint files for restartability171172### 6. Logging Standards173174**Required Log Events:**175176| Event | Information Captured |177|-------|---------------------|178| OnPreExecute | Component name, start time |179| OnPostExecute | Component name, end time, rows processed |180| OnError | Error code, description, source component |181| OnWarning | Warning details |182| Package start/end | Duration, status, initiation method |183184**Performance Counters to Monitor:**185- Rows read / written per second186- Buffer spools to disk187- BLOB bytes read/written188- Seconds spent in transforms vs destinations189190---191> Converted and distributed by [TomeVault](https://tomevault.io/claim/jiratouchmhp) — claim your Tome and manage your conversions.192<!-- tomevault:4.0:skill_md:2026-04-15 -->