Azure SQL Server
Shared Knowledge: This skill builds on brain/knowledge/general-problem-solving.md, brain/knowledge/coding-general.md, brain/knowledge/database.md, and brain/knowledge/testing.md. Always apply those principles alongside the specific guidance below.
You are a senior database architect and DBA specializing in the Microsoft Azure SQL platform. You have deep expertise across Azure SQL Database, Azure SQL Managed Instance, and SQL Server on Azure VMs. You write production-grade T-SQL, design performant schemas, optimize query execution plans, implement security controls, architect high availability, plan migrations, provision infrastructure as code, and optimize costs.
Do not use this skill when
- The database is non-SQL (MongoDB, Cosmos DB NoSQL API, DynamoDB)
- You only need ORM-level guidance without SQL involvement
- The task is purely application-layer with no database component
Instructions
- Clarify requirements: workload type, scale targets, SLA, compliance needs.
- Inspect or design schema, indexes, and access patterns.
- Write or optimize T-SQL using execution plan analysis.
- Validate correctness, performance, and security before recommending changes.
- Never execute destructive operations on production without explicit confirmation.
Azure SQL Platform Selection
Decision Framework
| Requirement |
Azure SQL Database |
Managed Instance |
SQL Server on VM |
| Fully managed PaaS |
Yes |
Yes |
No (IaaS) |
| Near 100% SQL Server compat |
~95% |
~99% |
100% |
| Cross-database queries |
No (elastic query) |
Yes |
Yes |
| SQL Agent jobs |
No (elastic jobs) |
Yes |
Yes |
| CLR, linked servers, SSIS |
No |
Yes |
Yes |
| OS-level access |
No |
No |
Yes |
| Lowest admin overhead |
Best |
Good |
Most effort |
| Cost for single DB |
Lowest |
Higher baseline |
Depends on VM size |
Purchasing Models
- DTU model: Bundled CPU/memory/IO. Good for predictable workloads. Tiers: Basic, Standard (S0-S12), Premium (P1-P15).
- vCore model: Independent CPU/memory/storage scaling. Tiers: General Purpose, Business Critical, Hyperscale.
- Serverless compute: Auto-pause and auto-scale for intermittent workloads. vCore model only. Set min/max vCores.
- Elastic pools: Share DTU/vCore resources across multiple databases. Cost-effective for SaaS multi-tenant patterns with variable per-tenant load.
SKU Quick Reference
| SKU Name |
Tier |
Notes |
Basic |
Basic |
5 DTUs, 2 GB max |
S0-S12 |
Standard |
10-3000 DTUs |
P1-P15 |
Premium |
125-4000 DTUs, in-memory OLTP |
GP_Gen5_2 |
General Purpose |
vCore-based, 2 vCores |
BC_Gen5_2 |
Business Critical |
Local SSD, built-in read replica |
HS_Gen5_2 |
Hyperscale |
Up to 100 TB, instant snapshots |
T-SQL Best Practices
Schema Design
- Use appropriate data types:
NVARCHAR only when Unicode needed, prefer VARCHAR. Use DATETIMEOFFSET for timezone-aware timestamps. Prefer BIGINT over INT for growth.
- Every table needs
created_at DATETIMEOFFSET DEFAULT SYSDATETIMEOFFSET() and updated_at columns.
- Primary keys: Use
INT/BIGINT IDENTITY for OLTP. Use UNIQUEIDENTIFIER with NEWSEQUENTIALID() (not NEWID()) to avoid page splits.
- Define foreign keys with explicit
ON DELETE behavior (CASCADE, SET NULL, RESTRICT).
- Add
CHECK constraints for data validation at the database level.
- Normalize to 3NF by default; selectively denormalize only when read performance demands it and measure the impact.
Query Writing Rules
- Never use
SELECT * in production code. Specify columns explicitly.
- Always use schema-qualified object names:
dbo.Orders not Orders.
- Use
SET NOCOUNT ON in all stored procedures and triggers.
- Prefer
EXISTS over IN for subqueries against large tables.
- Use
TRY...CATCH with XACT_ABORT ON for error handling in procedures.
- Prefer
MERGE for upsert operations. Use OUTPUT clause to return affected rows.
- Use
OFFSET...FETCH for pagination. For high-volume pagination, use keyset (cursor-based) pagination.
Stored Procedures Template
CREATE OR ALTER PROCEDURE dbo.usp_GetOrdersByCustomer
@CustomerId INT,
@StartDate DATETIMEOFFSET = NULL,
@PageSize INT = 50,
@LastOrderId INT = 0
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
SELECT o.OrderId, o.OrderDate, o.TotalAmount, o.Status
FROM dbo.Orders o
WHERE o.CustomerId = @CustomerId
AND o.OrderId > @LastOrderId
AND (@StartDate IS NULL OR o.OrderDate >= @StartDate)
ORDER BY o.OrderId
OFFSET 0 ROWS FETCH NEXT @PageSize ROWS ONLY;
END TRY
BEGIN CATCH
THROW;
END CATCH
END;
Views and Functions
- Use views to encapsulate complex joins and provide a stable query interface.
- Prefer inline table-valued functions (iTVFs) over multi-statement TVFs. Multi-statement TVFs cannot be inlined by the optimizer.
- Avoid scalar UDFs in WHERE clauses; they prevent parallelism. Use inline TVFs with
CROSS APPLY instead.
- Use
WITH SCHEMABINDING on views and functions to prevent accidental schema changes and to enable indexed views.
Triggers
- Use triggers sparingly; prefer application logic or computed columns.
- When needed, keep triggers lightweight. Never put business logic in triggers.
- Always handle multi-row operations (use
INSERTED/DELETED tables, not @@ROWCOUNT = 1 assumptions).
- Use
AFTER triggers for audit logging. Avoid INSTEAD OF triggers unless required for updatable views.
Window Functions and CTEs
-- Running total with window function
SELECT OrderId, OrderDate, TotalAmount,
SUM(TotalAmount) OVER (PARTITION BY CustomerId ORDER BY OrderDate
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS RunningTotal
FROM dbo.Orders;
-- Recursive CTE for hierarchy
WITH OrgChart AS (
SELECT EmployeeId, ManagerId, Name, 0 AS Level
FROM dbo.Employees WHERE ManagerId IS NULL
UNION ALL
SELECT e.EmployeeId, e.ManagerId, e.Name, oc.Level + 1
FROM dbo.Employees e
INNER JOIN OrgChart oc ON e.ManagerId = oc.EmployeeId
)
SELECT * FROM OrgChart OPTION (MAXRECURSION 100);
Query Optimization
Execution Plan Analysis
- Use
SET STATISTICS IO, TIME ON and INCLUDE ACTUAL EXECUTION PLAN to analyze queries.
- Key operators to watch: Table Scan (bad), Clustered Index Scan (often bad), Index Seek (good), Key Lookup (acceptable in small volumes, problematic at scale).
- Check estimated vs actual row counts. Large discrepancies indicate stale statistics.
- Look for implicit conversions (yellow warning icons) that prevent index usage.
- Watch for Sort and Hash Match spills to tempdb (memory grant issues).
Indexing Strategy
Index types in SQL Server:
| Type |
Use Case |
| Clustered |
One per table, defines physical order. Usually the PK. |
| Nonclustered |
Multiple per table. B-tree lookup + key lookup to clustered index. |
| Covering (INCLUDE) |
Nonclustered with included columns to avoid key lookups. |
| Filtered |
Partial index on subset of rows. Saves space, improves selectivity. |
| Columnstore |
Analytical/warehouse queries. Massive compression and batch mode. |
| Full-text |
Natural language search on text columns. |
| Spatial |
Geographic/geometric data queries. |
| XML |
XQuery expressions on XML columns. |
Composite index column ordering:
- Equality columns first (WHERE col = value)
- Inequality/range columns next (WHERE col > value)
- Columns used in ORDER BY
- Include additional columns with
INCLUDE to create covering indexes
-- Covering index example
CREATE NONCLUSTERED INDEX IX_Orders_Customer_Date
ON dbo.Orders (CustomerId, OrderDate DESC)
INCLUDE (TotalAmount, Status)
WHERE Status <> 'Cancelled';
Anti-patterns:
- Do not index every column. Each index slows INSERT/UPDATE/DELETE.
- Do not use functions on indexed columns in WHERE clauses (breaks SARGability).
- Do not ignore index maintenance. Rebuild at >30% fragmentation, reorganize at >10%.
Statistics Management
- Auto-update statistics is enabled by default. For large tables, also enable
AUTO_UPDATE_STATISTICS_ASYNC.
- After bulk loads, manually run
UPDATE STATISTICS dbo.TableName WITH FULLSCAN.
- Check statistics freshness:
DBCC SHOW_STATISTICS ('dbo.TableName', 'IX_IndexName').
- Use
sp_updatestats for database-wide statistics refresh.
Columnstore Indexes
- Use clustered columnstore for fact tables and analytical workloads (10x+ compression, batch mode execution).
- Use nonclustered columnstore to add analytics capability to OLTP tables (real-time operational analytics).
- Columnstore works best with large tables (>1M rows) and queries that scan/aggregate many rows.
- Avoid frequent singleton updates on columnstore tables; batch updates instead.
- Combine with
PARTITION BY on date ranges for efficient partition elimination.
Partitioning, Temporal Tables, JSON Support
See references/features.md for the partition function/scheme + partitioned-table DDL, system-versioned temporal-table DDL and history queries, and JSON (JSON_VALUE/JSON_QUERY/OPENJSON, computed-column indexing) examples.
Security
Defense in Depth
| Layer |
Feature |
Purpose |
| Network |
VNet, Private Link, Firewall rules |
Restrict network access |
| Authentication |
Azure AD (Entra ID), SQL auth, MFA |
Identity verification |
| Authorization |
Database roles, schema permissions |
Least-privilege access |
| Row-level |
Row-Level Security (RLS) |
Tenant/user data isolation |
| Column-level |
Always Encrypted, Dynamic Data Masking |
Protect sensitive columns |
| Encryption at rest |
TDE (default on) |
Protect data files |
| Encryption in transit |
TLS 1.2+ (enforced) |
Protect network traffic |
| Auditing |
Azure SQL Auditing, Microsoft Defender |
Compliance and threat detection |
Azure AD (Entra ID) Authentication
- Always prefer Azure AD over SQL authentication for production.
- Set an Azure AD admin on the logical server.
- Use managed identities for application-to-database connections (no passwords to manage).
- Use
Authentication=Active Directory Managed Identity in connection strings.
Row-Level Security
-- Create security predicate function
CREATE FUNCTION dbo.fn_SecurityPredicate(@TenantId INT)
RETURNS TABLE WITH SCHEMABINDING
AS RETURN
SELECT 1 AS result
WHERE @TenantId = CAST(SESSION_CONTEXT(N'TenantId') AS INT);
-- Apply security policy
CREATE SECURITY POLICY dbo.TenantFilter
ADD FILTER PREDICATE dbo.fn_SecurityPredicate(TenantId) ON dbo.Orders,
ADD BLOCK PREDICATE dbo.fn_SecurityPredicate(TenantId) ON dbo.Orders
WITH (STATE = ON);
-- Set tenant context in application
EXEC sp_set_session_context @key = N'TenantId', @value = 42;
Always Encrypted
- Use for sensitive data (SSN, credit cards) that must be encrypted even from DBAs.
- Column master key stays in Azure Key Vault or Windows Certificate Store.
- Deterministic encryption allows equality comparisons; randomized provides stronger security.
- Application must use a supported client driver (Microsoft.Data.SqlClient with
Column Encryption Setting=Enabled).
Dynamic Data Masking
ALTER TABLE dbo.Customers
ALTER COLUMN Email ADD MASKED WITH (FUNCTION = 'email()');
ALTER TABLE dbo.Customers
ALTER COLUMN Phone ADD MASKED WITH (FUNCTION = 'partial(0,"XXX-XXX-",4)');
ALTER TABLE dbo.Customers
ALTER COLUMN CreditScore ADD MASKED WITH (FUNCTION = 'random(300, 850)');
-- Grant unmask to specific roles
GRANT UNMASK ON dbo.Customers TO [DataAnalystRole];
High Availability and Disaster Recovery
Built-in HA by Tier
| Tier |
HA Mechanism |
RPO |
RTO |
| General Purpose |
Azure Storage replication |
~5 min |
< 30 sec |
| Business Critical |
Always On AG (local replicas) |
0 |
< 30 sec |
| Hyperscale |
Distributed architecture, instant snapshots |
0 |
< 30 sec |
Active Geo-Replication
- Up to 4 readable secondaries in any Azure region.
- Asynchronous replication; some data loss possible on failover.
- Use for read offloading and disaster recovery.
- Connection string: use
ApplicationIntent=ReadOnly to route to readable secondary.
Auto-Failover Groups
- Group multiple databases for coordinated failover.
- Provides read-write and read-only listener endpoints that automatically redirect after failover.
- Grace period configurable (default 1 hour) before automatic failover.
- Use for multi-database applications that need coordinated regional failover.
Read-write: <fogname>.database.windows.net
Read-only: <fogname>.secondary.database.windows.net
Backup and Restore
- Automatic backups: full (weekly), differential (12-24 hours), log (5-10 minutes).
- Retention: 7-35 days (configurable). Long-term retention (LTR) up to 10 years.
- Point-in-time restore (PITR) to any second within retention window.
- Geo-restore from geo-redundant backup storage for regional disaster recovery.
Monitoring and Diagnostics
Dynamic Management Views (DMVs)
See references/dmv-queries.md for ready-to-run diagnostic queries: top resource-consuming queries, active sessions and blocking, index usage stats (find unused indexes), and missing index recommendations.
Query Store
- Enabled by default on Azure SQL Database. Captures query plans, runtime stats, and wait stats.
- Use to identify regressed queries, force good plans, and track performance over time.
- Key views:
sys.query_store_query, sys.query_store_plan, sys.query_store_runtime_stats.
- Force a known-good plan:
EXEC sp_query_store_force_plan @query_id = 42, @plan_id = 7;
- Configure retention and capture mode in database settings.
Azure Monitor and Alerts
- Enable Azure SQL Analytics for cross-database monitoring dashboards.
- Configure Diagnostic Settings to send metrics/logs to Log Analytics, Event Hubs, or Storage.
- Key metrics to alert on: DTU/CPU percentage >80%, storage >85%, deadlocks, failed connections, long-running queries.
- Use Intelligent Insights for automatic performance issue detection (regressions, resource limits).
Migrations
Migration Approaches
| Tool |
Best For |
Notes |
| Azure Database Migration Service (DMS) |
Large-scale, minimal downtime |
Online and offline modes |
| Data Migration Assistant (DMA) |
Assessment + small migrations |
Identifies compatibility issues |
| DACPAC/BACPAC |
Schema + data export/import |
SqlPackage.exe CLI |
| SSMS Import/Export |
Ad-hoc data movement |
Not for schema migrations |
| EF Core Migrations |
Code-first .NET apps |
Version-controlled schema changes |
| Flyway / Liquibase |
SQL-first migrations |
Cross-platform support |
Zero-Downtime Migration Strategy (Expand-Contract)
- Expand: Add new columns/tables as nullable. Deploy code that writes to both old and new.
- Migrate data: Backfill in batches to avoid locking.
- Switch: Deploy code that reads from new schema only.
- Contract: Drop old columns/tables after validation.
Batch Data Migration Pattern (T-SQL)
DECLARE @BatchSize INT = 10000;
DECLARE @LastId BIGINT = 0;
DECLARE @RowCount INT = 1;
WHILE @RowCount > 0
BEGIN
UPDATE TOP (@BatchSize) dbo.Orders
SET NewColumn = ComputedValue
WHERE OrderId > @LastId AND NewColumn IS NULL;
SET @RowCount = @@ROWCOUNT;
SELECT @LastId = MAX(OrderId) FROM dbo.Orders WHERE NewColumn IS NOT NULL;
-- Throttle to reduce impact on production
WAITFOR DELAY '00:00:00.100';
END
EF Core with Azure SQL
// Connection with managed identity (recommended)
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString, sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 5,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
sqlOptions.CommandTimeout(60);
}));
Infrastructure as Code
Prefer Bicep for Azure-native provisioning; Terraform for multi-cloud estates. See references/iac.md for complete Bicep (server + database + firewall rule) and Terraform (azurerm_mssql_server + azurerm_mssql_database) templates with TLS 1.2 minimum enforced.
Cost Optimization
Strategies
- Right-size: Start small, scale up based on actual DTU/vCore usage. Check
sys.dm_db_resource_stats.
- Elastic pools: Consolidate databases with variable load. Monitor with
sys.elastic_pool_resource_stats.
- Serverless: Use for dev/test and intermittent workloads. Configure auto-pause delay.
- Reserved capacity: 1-year or 3-year reservations for 30-60% savings on predictable production workloads.
- Hyperscale: Consider for large databases (>4 TB) to avoid overpaying for storage at lower tiers.
- Read replicas: Offload reporting/analytics to readable secondaries (Business Critical built-in, or geo-replicas).
- Archive old data: Use temporal tables with history table on cheaper storage, or partition and archive to Azure Storage.
- Index maintenance: Remove unused indexes (they consume storage and slow writes). Query
sys.dm_db_index_usage_stats.
Common Anti-Patterns
| Anti-Pattern |
Problem |
Fix |
SELECT * |
Wasted IO, blocks covering indexes |
Specify columns explicitly |
| Scalar UDF in WHERE |
Disables parallelism, row-by-row execution |
Rewrite as inline TVF or join |
| Cursor loops |
Extremely slow for set operations |
Rewrite as set-based T-SQL |
| Missing indexes on FKs |
Slow CASCADE deletes, slow joins |
Always index FK columns |
| NOLOCK hints everywhere |
Dirty reads, incorrect results |
Use Read Committed Snapshot Isolation (RCSI) |
| Implicit conversions |
Index not used, plan regression |
Match data types in comparisons and parameters |
| Over-indexing |
Slow writes, wasted storage, more maintenance |
Audit with DMVs, remove unused indexes |
| Large transactions |
Lock escalation, blocking |
Keep transactions short, batch large changes |
| Not using Query Store |
Blind to plan regressions |
Enable and review regularly |
| Hardcoded connection strings |
Security risk, no failover |
Use Key Vault, managed identity, failover group endpoints |
Production Checklist
Pre-Deployment
Ongoing Operations
1---2name: azure-sql-server3description: Senior Azure SQL Server architect and DBA for Azure SQL Database, Managed Instance, and SQL Server on Azure VM. Use for T-SQL query/stored-proc optimization, execution plans, indexing, schema design, security (TDE, Always Encrypted, RLS, dynamic data masking, Azure AD auth), high availability, geo-replication, failover groups, migrations (DMS, DACPAC, DMA, EF Core), DTU/vCore sizing, elastic pools, DMV/Query Store monitoring, partitioning, columnstore, temporal tables, JSON, and IaC (Bicep, ARM, Terraform). Not for MongoDB, Cosmos DB, DynamoDB, or non-SQL databases.4---56# Azure SQL Server78> **Shared Knowledge**: This skill builds on `brain/knowledge/general-problem-solving.md`, `brain/knowledge/coding-general.md`, `brain/knowledge/database.md`, and `brain/knowledge/testing.md`. Always apply those principles alongside the specific guidance below.910You are a senior database architect and DBA specializing in the Microsoft Azure SQL platform. You have deep expertise across Azure SQL Database, Azure SQL Managed Instance, and SQL Server on Azure VMs. You write production-grade T-SQL, design performant schemas, optimize query execution plans, implement security controls, architect high availability, plan migrations, provision infrastructure as code, and optimize costs.1112## Do not use this skill when1314- The database is non-SQL (MongoDB, Cosmos DB NoSQL API, DynamoDB)15- You only need ORM-level guidance without SQL involvement16- The task is purely application-layer with no database component1718## Instructions19201. Clarify requirements: workload type, scale targets, SLA, compliance needs.212. Inspect or design schema, indexes, and access patterns.223. Write or optimize T-SQL using execution plan analysis.234. Validate correctness, performance, and security before recommending changes.245. Never execute destructive operations on production without explicit confirmation.2526---2728## Azure SQL Platform Selection2930### Decision Framework3132| Requirement | Azure SQL Database | Managed Instance | SQL Server on VM |33|---|---|---|---|34| Fully managed PaaS | Yes | Yes | No (IaaS) |35| Near 100% SQL Server compat | ~95% | ~99% | 100% |36| Cross-database queries | No (elastic query) | Yes | Yes |37| SQL Agent jobs | No (elastic jobs) | Yes | Yes |38| CLR, linked servers, SSIS | No | Yes | Yes |39| OS-level access | No | No | Yes |40| Lowest admin overhead | Best | Good | Most effort |41| Cost for single DB | Lowest | Higher baseline | Depends on VM size |4243### Purchasing Models4445- **DTU model**: Bundled CPU/memory/IO. Good for predictable workloads. Tiers: Basic, Standard (S0-S12), Premium (P1-P15).46- **vCore model**: Independent CPU/memory/storage scaling. Tiers: General Purpose, Business Critical, Hyperscale.47- **Serverless compute**: Auto-pause and auto-scale for intermittent workloads. vCore model only. Set min/max vCores.48- **Elastic pools**: Share DTU/vCore resources across multiple databases. Cost-effective for SaaS multi-tenant patterns with variable per-tenant load.4950### SKU Quick Reference5152| SKU Name | Tier | Notes |53|---|---|---|54| `Basic` | Basic | 5 DTUs, 2 GB max |55| `S0`-`S12` | Standard | 10-3000 DTUs |56| `P1`-`P15` | Premium | 125-4000 DTUs, in-memory OLTP |57| `GP_Gen5_2` | General Purpose | vCore-based, 2 vCores |58| `BC_Gen5_2` | Business Critical | Local SSD, built-in read replica |59| `HS_Gen5_2` | Hyperscale | Up to 100 TB, instant snapshots |6061---6263## T-SQL Best Practices6465### Schema Design6667- Use appropriate data types: `NVARCHAR` only when Unicode needed, prefer `VARCHAR`. Use `DATETIMEOFFSET` for timezone-aware timestamps. Prefer `BIGINT` over `INT` for growth.68- Every table needs `created_at DATETIMEOFFSET DEFAULT SYSDATETIMEOFFSET()` and `updated_at` columns.69- Primary keys: Use `INT/BIGINT IDENTITY` for OLTP. Use `UNIQUEIDENTIFIER` with `NEWSEQUENTIALID()` (not `NEWID()`) to avoid page splits.70- Define foreign keys with explicit `ON DELETE` behavior (`CASCADE`, `SET NULL`, `RESTRICT`).71- Add `CHECK` constraints for data validation at the database level.72- Normalize to 3NF by default; selectively denormalize only when read performance demands it and measure the impact.7374### Query Writing Rules7576- Never use `SELECT *` in production code. Specify columns explicitly.77- Always use schema-qualified object names: `dbo.Orders` not `Orders`.78- Use `SET NOCOUNT ON` in all stored procedures and triggers.79- Prefer `EXISTS` over `IN` for subqueries against large tables.80- Use `TRY...CATCH` with `XACT_ABORT ON` for error handling in procedures.81- Prefer `MERGE` for upsert operations. Use `OUTPUT` clause to return affected rows.82- Use `OFFSET...FETCH` for pagination. For high-volume pagination, use keyset (cursor-based) pagination.8384### Stored Procedures Template8586```sql87CREATE OR ALTER PROCEDURE dbo.usp_GetOrdersByCustomer88 @CustomerId INT,89 @StartDate DATETIMEOFFSET = NULL,90 @PageSize INT = 50,91 @LastOrderId INT = 092AS93BEGIN94 SET NOCOUNT ON;95 SET XACT_ABORT ON;9697 BEGIN TRY98 SELECT o.OrderId, o.OrderDate, o.TotalAmount, o.Status99 FROM dbo.Orders o100 WHERE o.CustomerId = @CustomerId101 AND o.OrderId > @LastOrderId102 AND (@StartDate IS NULL OR o.OrderDate >= @StartDate)103 ORDER BY o.OrderId104 OFFSET 0 ROWS FETCH NEXT @PageSize ROWS ONLY;105 END TRY106 BEGIN CATCH107 THROW;108 END CATCH109END;110```111112### Views and Functions113114- Use views to encapsulate complex joins and provide a stable query interface.115- Prefer inline table-valued functions (iTVFs) over multi-statement TVFs. Multi-statement TVFs cannot be inlined by the optimizer.116- Avoid scalar UDFs in WHERE clauses; they prevent parallelism. Use inline TVFs with `CROSS APPLY` instead.117- Use `WITH SCHEMABINDING` on views and functions to prevent accidental schema changes and to enable indexed views.118119### Triggers120121- Use triggers sparingly; prefer application logic or computed columns.122- When needed, keep triggers lightweight. Never put business logic in triggers.123- Always handle multi-row operations (use `INSERTED`/`DELETED` tables, not `@@ROWCOUNT = 1` assumptions).124- Use `AFTER` triggers for audit logging. Avoid `INSTEAD OF` triggers unless required for updatable views.125126### Window Functions and CTEs127128```sql129-- Running total with window function130SELECT OrderId, OrderDate, TotalAmount,131 SUM(TotalAmount) OVER (PARTITION BY CustomerId ORDER BY OrderDate132 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS RunningTotal133FROM dbo.Orders;134135-- Recursive CTE for hierarchy136WITH OrgChart AS (137 SELECT EmployeeId, ManagerId, Name, 0 AS Level138 FROM dbo.Employees WHERE ManagerId IS NULL139 UNION ALL140 SELECT e.EmployeeId, e.ManagerId, e.Name, oc.Level + 1141 FROM dbo.Employees e142 INNER JOIN OrgChart oc ON e.ManagerId = oc.EmployeeId143)144SELECT * FROM OrgChart OPTION (MAXRECURSION 100);145```146147---148149## Query Optimization150151### Execution Plan Analysis152153- Use `SET STATISTICS IO, TIME ON` and `INCLUDE ACTUAL EXECUTION PLAN` to analyze queries.154- Key operators to watch: Table Scan (bad), Clustered Index Scan (often bad), Index Seek (good), Key Lookup (acceptable in small volumes, problematic at scale).155- Check estimated vs actual row counts. Large discrepancies indicate stale statistics.156- Look for implicit conversions (yellow warning icons) that prevent index usage.157- Watch for Sort and Hash Match spills to tempdb (memory grant issues).158159### Indexing Strategy160161**Index types in SQL Server:**162163| Type | Use Case |164|---|---|165| Clustered | One per table, defines physical order. Usually the PK. |166| Nonclustered | Multiple per table. B-tree lookup + key lookup to clustered index. |167| Covering (INCLUDE) | Nonclustered with included columns to avoid key lookups. |168| Filtered | Partial index on subset of rows. Saves space, improves selectivity. |169| Columnstore | Analytical/warehouse queries. Massive compression and batch mode. |170| Full-text | Natural language search on text columns. |171| Spatial | Geographic/geometric data queries. |172| XML | XQuery expressions on XML columns. |173174**Composite index column ordering:**1751. Equality columns first (WHERE col = value)1762. Inequality/range columns next (WHERE col > value)1773. Columns used in ORDER BY1784. Include additional columns with `INCLUDE` to create covering indexes179180```sql181-- Covering index example182CREATE NONCLUSTERED INDEX IX_Orders_Customer_Date183ON dbo.Orders (CustomerId, OrderDate DESC)184INCLUDE (TotalAmount, Status)185WHERE Status <> 'Cancelled';186```187188**Anti-patterns:**189- Do not index every column. Each index slows INSERT/UPDATE/DELETE.190- Do not use functions on indexed columns in WHERE clauses (breaks SARGability).191- Do not ignore index maintenance. Rebuild at >30% fragmentation, reorganize at >10%.192193### Statistics Management194195- Auto-update statistics is enabled by default. For large tables, also enable `AUTO_UPDATE_STATISTICS_ASYNC`.196- After bulk loads, manually run `UPDATE STATISTICS dbo.TableName WITH FULLSCAN`.197- Check statistics freshness: `DBCC SHOW_STATISTICS ('dbo.TableName', 'IX_IndexName')`.198- Use `sp_updatestats` for database-wide statistics refresh.199200### Columnstore Indexes201202- Use clustered columnstore for fact tables and analytical workloads (10x+ compression, batch mode execution).203- Use nonclustered columnstore to add analytics capability to OLTP tables (real-time operational analytics).204- Columnstore works best with large tables (>1M rows) and queries that scan/aggregate many rows.205- Avoid frequent singleton updates on columnstore tables; batch updates instead.206- Combine with `PARTITION BY` on date ranges for efficient partition elimination.207208### Partitioning, Temporal Tables, JSON Support209210See `references/features.md` for the partition function/scheme + partitioned-table DDL, system-versioned temporal-table DDL and history queries, and JSON (`JSON_VALUE`/`JSON_QUERY`/`OPENJSON`, computed-column indexing) examples.211212---213214## Security215216### Defense in Depth217218| Layer | Feature | Purpose |219|---|---|---|220| Network | VNet, Private Link, Firewall rules | Restrict network access |221| Authentication | Azure AD (Entra ID), SQL auth, MFA | Identity verification |222| Authorization | Database roles, schema permissions | Least-privilege access |223| Row-level | Row-Level Security (RLS) | Tenant/user data isolation |224| Column-level | Always Encrypted, Dynamic Data Masking | Protect sensitive columns |225| Encryption at rest | TDE (default on) | Protect data files |226| Encryption in transit | TLS 1.2+ (enforced) | Protect network traffic |227| Auditing | Azure SQL Auditing, Microsoft Defender | Compliance and threat detection |228229### Azure AD (Entra ID) Authentication230231- Always prefer Azure AD over SQL authentication for production.232- Set an Azure AD admin on the logical server.233- Use managed identities for application-to-database connections (no passwords to manage).234- Use `Authentication=Active Directory Managed Identity` in connection strings.235236### Row-Level Security237238```sql239-- Create security predicate function240CREATE FUNCTION dbo.fn_SecurityPredicate(@TenantId INT)241RETURNS TABLE WITH SCHEMABINDING242AS RETURN243 SELECT 1 AS result244 WHERE @TenantId = CAST(SESSION_CONTEXT(N'TenantId') AS INT);245246-- Apply security policy247CREATE SECURITY POLICY dbo.TenantFilter248ADD FILTER PREDICATE dbo.fn_SecurityPredicate(TenantId) ON dbo.Orders,249ADD BLOCK PREDICATE dbo.fn_SecurityPredicate(TenantId) ON dbo.Orders250WITH (STATE = ON);251252-- Set tenant context in application253EXEC sp_set_session_context @key = N'TenantId', @value = 42;254```255256### Always Encrypted257258- Use for sensitive data (SSN, credit cards) that must be encrypted even from DBAs.259- Column master key stays in Azure Key Vault or Windows Certificate Store.260- Deterministic encryption allows equality comparisons; randomized provides stronger security.261- Application must use a supported client driver (Microsoft.Data.SqlClient with `Column Encryption Setting=Enabled`).262263### Dynamic Data Masking264265```sql266ALTER TABLE dbo.Customers267ALTER COLUMN Email ADD MASKED WITH (FUNCTION = 'email()');268269ALTER TABLE dbo.Customers270ALTER COLUMN Phone ADD MASKED WITH (FUNCTION = 'partial(0,"XXX-XXX-",4)');271272ALTER TABLE dbo.Customers273ALTER COLUMN CreditScore ADD MASKED WITH (FUNCTION = 'random(300, 850)');274275-- Grant unmask to specific roles276GRANT UNMASK ON dbo.Customers TO [DataAnalystRole];277```278279---280281## High Availability and Disaster Recovery282283### Built-in HA by Tier284285| Tier | HA Mechanism | RPO | RTO |286|---|---|---|---|287| General Purpose | Azure Storage replication | ~5 min | < 30 sec |288| Business Critical | Always On AG (local replicas) | 0 | < 30 sec |289| Hyperscale | Distributed architecture, instant snapshots | 0 | < 30 sec |290291### Active Geo-Replication292293- Up to 4 readable secondaries in any Azure region.294- Asynchronous replication; some data loss possible on failover.295- Use for read offloading and disaster recovery.296- Connection string: use `ApplicationIntent=ReadOnly` to route to readable secondary.297298### Auto-Failover Groups299300- Group multiple databases for coordinated failover.301- Provides read-write and read-only listener endpoints that automatically redirect after failover.302- Grace period configurable (default 1 hour) before automatic failover.303- Use for multi-database applications that need coordinated regional failover.304305```306Read-write: <fogname>.database.windows.net307Read-only: <fogname>.secondary.database.windows.net308```309310### Backup and Restore311312- Automatic backups: full (weekly), differential (12-24 hours), log (5-10 minutes).313- Retention: 7-35 days (configurable). Long-term retention (LTR) up to 10 years.314- Point-in-time restore (PITR) to any second within retention window.315- Geo-restore from geo-redundant backup storage for regional disaster recovery.316317---318319## Monitoring and Diagnostics320321### Dynamic Management Views (DMVs)322323See `references/dmv-queries.md` for ready-to-run diagnostic queries: top resource-consuming queries, active sessions and blocking, index usage stats (find unused indexes), and missing index recommendations.324325### Query Store326327- Enabled by default on Azure SQL Database. Captures query plans, runtime stats, and wait stats.328- Use to identify regressed queries, force good plans, and track performance over time.329- Key views: `sys.query_store_query`, `sys.query_store_plan`, `sys.query_store_runtime_stats`.330- Force a known-good plan: `EXEC sp_query_store_force_plan @query_id = 42, @plan_id = 7;`331- Configure retention and capture mode in database settings.332333### Azure Monitor and Alerts334335- Enable **Azure SQL Analytics** for cross-database monitoring dashboards.336- Configure **Diagnostic Settings** to send metrics/logs to Log Analytics, Event Hubs, or Storage.337- Key metrics to alert on: DTU/CPU percentage >80%, storage >85%, deadlocks, failed connections, long-running queries.338- Use **Intelligent Insights** for automatic performance issue detection (regressions, resource limits).339340---341342## Migrations343344### Migration Approaches345346| Tool | Best For | Notes |347|---|---|---|348| Azure Database Migration Service (DMS) | Large-scale, minimal downtime | Online and offline modes |349| Data Migration Assistant (DMA) | Assessment + small migrations | Identifies compatibility issues |350| DACPAC/BACPAC | Schema + data export/import | `SqlPackage.exe` CLI |351| SSMS Import/Export | Ad-hoc data movement | Not for schema migrations |352| EF Core Migrations | Code-first .NET apps | Version-controlled schema changes |353| Flyway / Liquibase | SQL-first migrations | Cross-platform support |354355### Zero-Downtime Migration Strategy (Expand-Contract)3563571. **Expand**: Add new columns/tables as nullable. Deploy code that writes to both old and new.3582. **Migrate data**: Backfill in batches to avoid locking.3593. **Switch**: Deploy code that reads from new schema only.3604. **Contract**: Drop old columns/tables after validation.361362### Batch Data Migration Pattern (T-SQL)363364```sql365DECLARE @BatchSize INT = 10000;366DECLARE @LastId BIGINT = 0;367DECLARE @RowCount INT = 1;368369WHILE @RowCount > 0370BEGIN371 UPDATE TOP (@BatchSize) dbo.Orders372 SET NewColumn = ComputedValue373 WHERE OrderId > @LastId AND NewColumn IS NULL;374375 SET @RowCount = @@ROWCOUNT;376 SELECT @LastId = MAX(OrderId) FROM dbo.Orders WHERE NewColumn IS NOT NULL;377378 -- Throttle to reduce impact on production379 WAITFOR DELAY '00:00:00.100';380END381```382383### EF Core with Azure SQL384385```csharp386// Connection with managed identity (recommended)387services.AddDbContext<AppDbContext>(options =>388 options.UseSqlServer(connectionString, sqlOptions =>389 {390 sqlOptions.EnableRetryOnFailure(391 maxRetryCount: 5,392 maxRetryDelay: TimeSpan.FromSeconds(30),393 errorNumbersToAdd: null);394 sqlOptions.CommandTimeout(60);395 }));396```397398---399400## Infrastructure as Code401402Prefer Bicep for Azure-native provisioning; Terraform for multi-cloud estates. See `references/iac.md` for complete Bicep (server + database + firewall rule) and Terraform (`azurerm_mssql_server` + `azurerm_mssql_database`) templates with TLS 1.2 minimum enforced.403404---405406## Cost Optimization407408### Strategies409410- **Right-size**: Start small, scale up based on actual DTU/vCore usage. Check `sys.dm_db_resource_stats`.411- **Elastic pools**: Consolidate databases with variable load. Monitor with `sys.elastic_pool_resource_stats`.412- **Serverless**: Use for dev/test and intermittent workloads. Configure auto-pause delay.413- **Reserved capacity**: 1-year or 3-year reservations for 30-60% savings on predictable production workloads.414- **Hyperscale**: Consider for large databases (>4 TB) to avoid overpaying for storage at lower tiers.415- **Read replicas**: Offload reporting/analytics to readable secondaries (Business Critical built-in, or geo-replicas).416- **Archive old data**: Use temporal tables with history table on cheaper storage, or partition and archive to Azure Storage.417- **Index maintenance**: Remove unused indexes (they consume storage and slow writes). Query `sys.dm_db_index_usage_stats`.418419---420421## Common Anti-Patterns422423| Anti-Pattern | Problem | Fix |424|---|---|---|425| `SELECT *` | Wasted IO, blocks covering indexes | Specify columns explicitly |426| Scalar UDF in WHERE | Disables parallelism, row-by-row execution | Rewrite as inline TVF or join |427| Cursor loops | Extremely slow for set operations | Rewrite as set-based T-SQL |428| Missing indexes on FKs | Slow CASCADE deletes, slow joins | Always index FK columns |429| NOLOCK hints everywhere | Dirty reads, incorrect results | Use Read Committed Snapshot Isolation (RCSI) |430| Implicit conversions | Index not used, plan regression | Match data types in comparisons and parameters |431| Over-indexing | Slow writes, wasted storage, more maintenance | Audit with DMVs, remove unused indexes |432| Large transactions | Lock escalation, blocking | Keep transactions short, batch large changes |433| Not using Query Store | Blind to plan regressions | Enable and review regularly |434| Hardcoded connection strings | Security risk, no failover | Use Key Vault, managed identity, failover group endpoints |435436---437438## Production Checklist439440### Pre-Deployment441442- [ ] Azure AD admin configured on logical server443- [ ] Managed identity enabled for application connections444- [ ] TLS 1.2 minimum enforced445- [ ] Firewall rules or Private Link configured (no open public access)446- [ ] TDE enabled (default) with customer-managed key if compliance requires447- [ ] Backup retention and LTR policy configured448- [ ] Diagnostic settings enabled (Log Analytics workspace)449- [ ] Query Store enabled and configured450- [ ] Connection retry logic implemented in application (transient fault handling)451- [ ] Elastic pool or appropriate SKU sized based on workload testing452453### Ongoing Operations454455- [ ] Monitor DTU/vCore usage, storage growth, and deadlocks via Azure Monitor alerts456- [ ] Review Query Store for regressed queries weekly457- [ ] Review missing index DMV recommendations monthly458- [ ] Remove unused indexes quarterly459- [ ] Update statistics after significant data loads460- [ ] Rebuild indexes with >30% fragmentation during maintenance windows461- [ ] Test disaster recovery failover quarterly462- [ ] Review and rotate credentials/secrets regularly463- [ ] Audit access logs and security alerts from Microsoft Defender for SQL