Status: Reviewed (deep-dive v1)
Stack note: PowerShell collectors and Blazor Server should talk to SQL through usp_* procs — not ad-hoc SQL, not scalar UDFs as the API.
Pairs with: SQL-11 (UDFs stay inside T-SQL), SQL-13 (#temp staging in procs), SPIKE-01…08 board shapes.
Goal: Authoring, calling, security, and hygiene patterns for the maintenance warehouse API.
| Need | Do this |
|---|---|
| Blazor page needs data or an action | One proc per use-case (usp_Alert_GetOpenBoard, usp_Alert_Ack) |
| Reuse a query shape in many procs | View or inline TVF — not a second “API” UDF |
| Multi-step staging | Proc + #temp (SQL-13) |
| Filter list from UI | TVP or typed params — not concatenated SQL |
| Job / Agent automation | Same procs Blazor uses, or thin wrappers |
usp_<Area>_<Verb><Object>
usp_Inventory_GetInstances
usp_Backup_GetHeatmap
usp_Alert_Evaluate -- Agent-facing
usp_Alert_Ack -- UI-facing write
dbo or domain (alert, inv) — pick one convention and stick /sql/<area>/00N_procs.sql (SPIKE layout) ALTER in prod — deploy via source control CREATE OR ALTER PROC dbo.usp_Example_GetBoard
@SqlInstanceId int = NULL,
@UnackedOnly bit = 0
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON; -- optional but good for write procs
-- reads: no outer tran needed
SELECT …
FROM …
WHERE (@SqlInstanceId IS NULL OR i.SqlInstanceId = @SqlInstanceId)
AND (@UnackedOnly = 0 OR a.Status = 'Open');
END;
Checklist on create:
SET NOCOUNT ON (cuts done-in-proc chatter; Blazor/ADO happier) SELECT * on wide warehouse tables — name columns the UI needs TRY/CATCH + clear success/rowcount (see SQL-15 when published) | Kind | Examples | Notes |
|---|---|---|
| Read board | GetOpenBoard, GetHeatmap, GetOutOfDate |
Filter params; ORDER BY for UI; no side effects |
| Read detail | GetDetail @Id |
Single entity + child rows (events) |
| Command | Ack, Evaluate |
Validate state; write + optional event row; no kill/revoke/patch from UI |
| Merge/load | Collector merge from PS | Idempotent upsert; run under Agent/PS credential |
Hard rule (SPIKE): UI procs never start Agent jobs, revoke logins, or run backups — boards are observe + ack only.
@AckedBy) — don’t trust a client-only field without auth context Evaluate may need longer (or Agent-only) db_owner // shape only — CommandType.StoredProcedure, parameters for @UnackedOnly etc.
Invoke-Sqlcmd -ServerInstance $Wh -Database $Db `
-Query 'EXEC dbo.usp_Alert_Evaluate' # or AddWithValue params for filters
Collectors: bulk load staging → usp_*_Merge. Don’t embed giant scripts in PS when a proc owns the logic.
NULL means “all” (document it) READONLY) — large lists → dump to #temp then join (SQL-13) GRANT EXECUTE ON dbo.usp_… TO [AppRole] — least privilege Evaluate / merge: Agent or ops role only WHERE; indexes for board filters (SQL-16 shortlist) #temp, not table vars OPTION (RECOMPILE) or local vars only when measured usp_* | Symptom | Likely cause |
|---|---|
| Blazor timeout on board | Fat join / missing index / Evaluate called from UI thread |
| Extra empty result sets | Missing SET NOCOUNT ON or debug SELECT left in |
| Wrong user on Ack | @AckedBy not from auth; client spoof |
| Works in SSMS, fails in app | Different login rights; default DB; ansi settings |
| “API” is a scalar UDF | Wrong layer — wrap as proc |
| Area | Example procs |
|---|---|
| Inventory | usp_Inventory_GetInstances, merge from PS |
| Backup | usp_Backup_GetHeatmap |
| Agent | usp_Agent_GetFailBoard |
| Alerts | usp_Alert_Evaluate, usp_Alert_GetOpenBoard, usp_Alert_Ack |
| Capacity / perf / security / build | Same Get* board pattern |
SET NOCOUNT + grant checklist in PR template SQL Dude — SQL-12 Stored Procedures v1