// RetailVault · SQL Performance Lab
Azure SQL · 100k rows · 7 indexes · 7 stored procedures · automated maintenance · engine internals
Elapsed time comparison — before vs after index tuning
| Query | Before | After | Improvement | Visual |
|---|---|---|---|---|
| KPI summary | 191 ms | 50 ms | 74% | |
| Monthly sales | 233 ms | 50 ms | 79% | |
| Top products | 233 ms | 50 ms | 79% | |
| Low stock alerts | 233 ms | 30 ms | 87% | |
| Inventory turnover | 11,618 ms | 692 ms | 94% |
Logical reads per table — before vs after
idx_lab_fs_date_product
fact_sales · keys: date_key, product_key
Coveringidx_lab_fs_store_date
fact_sales · keys: store_key, date_key
Coveringidx_lab_fi_below_reorder
fact_inventory · WHERE is_below_reorder = 1
Filteredidx_lab_fi_product_date
fact_inventory · keys: product_key, store_key
Coveringidx_lab_fi_product_agg
fact_inventory · keys: product_key, date_key
Coveringidx_lab_dimdate_year_month
dim_date · keys: year, month_num, quarter
Coveringidx_lab_dp_category_price
dim_product · keys: category_name, is_current
Covering| Procedure | Purpose | Key optimization | Tag |
|---|---|---|---|
| usp_GetKpiSummary | Revenue, profit, margin for a given year | Covering index seek on fact_sales | Analytics |
| usp_GetMonthlySales | Month-by-month trend with running total | Window function + idx_lab_fs_date_product | Analytics |
| usp_GetTopProducts | Top N products by revenue, parameterized | CTE + covering index, 95% dim reads reduced | Analytics |
| usp_GetLowStockAlerts | Items below reorder level across all stores | Filtered index — only reads is_below_reorder=1 | Inventory |
| usp_GetInventoryTurnover | Stock velocity ratio per product | OPTION(RECOMPILE) — eliminates param sniffing | Inventory |
| usp_MaintainIndexes | Auto-rebuild / reorganize based on fragmentation | Dynamic SQL + sys.dm_db_index_physical_stats | DBA |
| usp_GetIndexHealth | Fragmentation report for all indexes | DMV query — REBUILD / REORGANIZE / HEALTHY | DBA |
§D — Top 15 wait statistics · Azure SQL instance
| Wait type | Tasks | Total wait (ms) | Max wait (ms) | Signal wait % | Class |
|---|---|---|---|---|---|
| SOS_WORK_DISPATCHER | 258,620 | 1,429,396,595 | 12,071,450 | 0.0% | infra |
| PREEMPTIVE_XE_DISPATCHER | 634 | 65,078,821 | 773,544 | 0.0% | infra |
| DIRTY_PAGE_POLL | 338,950 | 36,619,190 | 228 | 0.0% | infra |
| QDS_PERSIST_TASK_MAIN_LOOP_SLEEP | 611 | 36,604,461 | 60,016 | 0.0% | infra |
| PWAIT_EXTENSIBILITY_CLEANUP_TASK | 123 | 36,600,795 | 300,015 | 100.0% | infra |
| XE_LIVE_TARGET_TVF | 610 | 36,544,320 | 60,016 | 0.0% | infra |
| QDS_ASYNC_QUEUE | 64 | 36,277,280 | 12,597,167 | 0.0% | infra |
| HADR_FABRIC_CALLBACK | 5,071 | 36,010,202 | 1,800,015 | 0.0% | infra |
| PVS_PREALLOCATE | 136 | 35,402,747 | 17,486,868 | 0.0% | infra |
| HADR_TIMER_TASK | 954 | 9,560,680 | 20,026 | 0.0% | infra |
| WRITELOG | 114,283 | 574,525 | 533 | 3.7% | app |
| LOG_RATE_GOVERNOR | 22,239 | 348,383 | 102 | 0.1% | app |
| RESOURCE_GOVERNOR_IDLE | 16,712 | 217,558 | 35 | 0.0% | infra |
| SOS_SCHEDULER_YIELD | 13,394 | 208,192 | 279 | 100.0% | app |
| PAGEIOLATCH_SH | 4,875 | 185,195 | 1,127 | 1.7% | app |
All top waits are Azure SQL infrastructure threads (HA fabric, Query Store, checkpoint). Application-level waits (WRITELOG, PAGEIOLATCH_SH) are negligible — no blocking, no memory pressure, no IO bottleneck.
§E — Index usage stats · seeks vs scans since last restart
| Table | Index | Type | Seeks | Scans | Updates | Status |
|---|---|---|---|---|---|---|
| dim_date | PK (clustered) | CLUSTERED | 114,259 | 44 | 1,246 | active |
| dim_product | PK (clustered) | CLUSTERED | 211,451 | 29 | 1 | active |
| dim_store | PK (clustered) | CLUSTERED | 110,941 | 5 | 1 | active |
| dim_date | idx_lab_dimdate_year_month | NONCLUSTERED | 9 | 0 | 0 | active |
| dim_product | idx_lab_dp_category_price | NONCLUSTERED | 0 | 8 | 0 | scan-only |
| fact_sales | idx_fs_date_store | NONCLUSTERED | 7 | 28 | 100,501 | active |
| fact_sales | idx_fs_product | NONCLUSTERED | 20 | 36 | 100,501 | active |
| fact_sales | idx_lab_fs_date_product | NONCLUSTERED | 0 | 15 | 0 | scan-only |
| fact_sales | idx_lab_fs_store_date | NONCLUSTERED | 0 | 4 | 100,501 | scan-only |
| fact_inventory | idx_lab_fi_product_agg | NONCLUSTERED | 0 | 24 | 10,401 | scan-only |
| fact_inventory | idx_lab_fi_product_date | NONCLUSTERED | 0 | 26 | 10,401 | scan-only |
| fact_inventory | idx_lab_fi_below_reorder | NONCLUSTERED | 0 | 3 | 0 | scan-only |
| fact_inventory | idx_fi_date_store | NONCLUSTERED | 0 | 0 | 0 | scan-only |
Scan-only indexes are expected at 100k rows / low cardinality (20 products, 10 stores) — optimizer correctly prefers scans. At production scale these indexes will seek. No indexes flagged write-only; all recommendations: OK.