Fake EDW schema reference
This page is the SAIR project contract. It lives under docs/projects/sair/ so a later project mock can follow the same pattern: docs/projects/<project>/.
The contract is invented in this repo. Real customer Ellipse/EDW DDL is operator-supplied and is not here. Column names follow what NeuralOps SAIR workflows consume (site, plant_no, EQUIP_NO / equipment, work_order, actual_finish, …).
How the tables and views are applied
sql-estate/databases/edw/02_schema.sql is the script that creates the base tables (CREATE TABLE, guarded by IF NOT EXISTS) and the seven views (CREATE OR ALTER VIEW). 01_create_database.sql creates the edw database when it is missing. 03_seed.sql MERGEs rows back to the git baseline.
sql-estate/seed/apply.sh runs those scripts in lexical order:
- Local:
docker compose up -d --wait. Thesql-estate-seedsidecar runsapply.shafter SQL Server is healthy. Re-running is safe; the scripts are idempotent. - Azure: Bicep creates the database during operator
azd up. Postprovision, and.github/workflows/azure-estate.ymlon merge tomain(orworkflow_dispatchonmain), run the sameapply.shwithSQLCMD_AUTH=aadandSQLCMD_SKIP_CREATE_DATABASE=1. That skips01_create_database.sqland re-applies02_schema.sqland03_seed.sql. A docs-only merge does not seed.
Scripts directory: sql-estate/databases/edw/.
QDox correlation: vw_WorkOrder_Enriched.work_order and .EQUIP_NO match
WorkOrder / equipment on catalogue rows in
fake-host/app/personalities/qdox/fixtures/catalogue.json.
equipment is never sourced from QDox (NeuralOps QDoxClient contract);
it always comes from this SQL row.
Keys
Every view is filterable by (site, plant_no) — the SAIR run inputs. Equipment
identity is EQUIP_NO (Ellipse-style), with (site, plant_no, EQUIP_NO) as the
asset key. equipment is a view alias of EQUIP_NO
for consumers that SELECT site, plant_no, equipment, work_order.
SQL Server's default CI collation treats EQUIP_NO and equip_no as the same
identifier, so views do not duplicate case-folded names. Python clients that
lowercase keys still match EQUIP_NO → equip_no.
Site and plant hierarchy (seed)
The estate uses site SA001, two-segment process scope SA001-0100,
three-segment pump type scope SA001-0100-PMP, and four-segment asset slots.
PLANT_NO_BASE stores the row's canonical full slot; family scopes are derived,
not stored as alternate subject identifiers.
| plant_no | Role |
|---|---|
SA001-0100 |
Process parent |
SA001-0100-PMP-0001 |
Subject slot |
SA001-0100-PMP-0002 |
In-service same-type sibling |
SA001-0100-VLV-0001 |
In-service different-type child |
The subject slot has exactly one IS occupant (71689591009919). The other
workbook equipment identifiers are historical DI, DC, or SP lineage rows.
All nine QDox-backed work orders remain attached to their original workbook
EQUIP_NO and now share the subject slot.
Three EDW-only rows exercise graceful degradation and family aggregation:
EDGE-INSPECT (subject, no document, zero cost), EDGE-SIBLING, and
EDGE-VALVE. They do not expand the nine-file QDox corpus.
Views
dbo.vw_WorkOrder_Enriched
Enriched work-order rows for SAIR ingest/report (edw_workorder_enriched,
edw_workorder_enriched_family).
| Column | Meaning |
|---|---|
site, plant_no |
SAIR keys |
EQUIP_NO, equipment |
Asset id (alias pair) |
work_order |
Work-order number (QDox WorkOrder) |
wo_desc, wo_type, wo_status, maint_type, priority |
Ellipse-style WO fields. maint_type is Planned or Reactive. |
failure_code |
FK-ish to vw_Failure_Codes.failure_code |
actual_start, actual_finish, sched_start, sched_finish |
Datetimes; actual_finish NULL means not completed |
has_documents |
1 when the QDox catalogue has at least one Type:2 record |
cost_total |
Sum of labour + material + contractor |
dbo.vw_Site_Equipment
Asset register (edw_site_equipment, edw_equipment_family).
| Column | Meaning |
|---|---|
site, plant_no |
SAIR keys |
EQUIP_NO, equipment |
Asset id |
PARENT_EQUIP, parent_equipment |
Parent in the site hierarchy (NULL for headers) |
PLANT_NO_BASE |
Canonical plant slot (full four segments for real assets) |
EQUIP_NAME, EQPT_TYPE, EQPT_TYPE_DESC |
PMP/VLV/HDR + description |
EQUIP_STATUS |
Lifecycle/register status (IS = in service). Not runtime state. |
DIVISION, SCHEME, PROCESS_AREA, LOCATION |
Registered facts for process-function inference |
equip_class, equip_class_desc, manufacturer, model_number, serial_number, creation_date, equip_criticality |
Wide Asset Profile fields; unknown facts remain null |
dbo.vw_WorkOrder_Cost_Totals
Per-WO cost breakdown (edw_workorder_cost_totals).
| Column | Meaning |
|---|---|
site, plant_no, EQUIP_NO, work_order |
Keys |
labour_cost, material_cost, contractor_cost |
Components |
total_cost |
Sum. Zero on WO 12346. |
dbo.vw_Maintenance_Schedule
Preventive schedules (edw_maintenance_schedule).
| Column | Meaning |
|---|---|
site, plant_no, EQUIP_NO |
Keys |
schedule_id, schedule_desc, schedule_type |
PM identity |
freq_days, last_done, next_due |
Cadence |
dbo.vw_Failure_Codes
Failure-code catalogue crossed onto every (site, plant_no) so plant-scoped
probes return rows (edw_failure_codes).
| Column | Meaning |
|---|---|
site, plant_no |
Scope |
failure_code, failure_desc, failure_class |
Mechanical / Electrical |
dbo.vw_CrossAsset_Peer_Metrics
Peer stats by EQPT_TYPE attached to each asset. NeuralOps report-gen no longer
registers this source (metrics are computed client-side), but the view remains
the estate contract so probes and older consumers still work.
| Column | Meaning |
|---|---|
site, plant_no, EQUIP_NO, EQPT_TYPE |
Keys |
peer_count |
Assets of the same type with completed work orders |
avg_wo_count, avg_cost, mtbf_days |
Type-level averages |
dbo.vw_Asset_Attributes
Name/value attributes (edw_asset_attributes).
| Column | Meaning |
|---|---|
site, plant_no, EQUIP_NO |
Keys |
attr_name, attr_value, attr_uom |
e.g. DesignFlow / 45 / L/s |
Probe queries
Candidates for NeuralOps source probe_query (and laptop smoke after azd up).
Each must return ≥1 row against the git seed.
-- vw_WorkOrder_Enriched
SELECT TOP (5) site, plant_no, EQUIP_NO, equipment, work_order, actual_finish, cost_total, has_documents
FROM dbo.vw_WorkOrder_Enriched
WHERE site = N'SA001' AND plant_no = N'SA001-0100-PMP-0001';
-- vw_Site_Equipment
SELECT TOP (5) site, plant_no, EQUIP_NO, PARENT_EQUIP, EQPT_TYPE_DESC, EQUIP_STATUS
FROM dbo.vw_Site_Equipment
WHERE site = N'SA001';
-- vw_WorkOrder_Cost_Totals
SELECT TOP (5) site, plant_no, work_order, labour_cost, material_cost, contractor_cost, total_cost
FROM dbo.vw_WorkOrder_Cost_Totals
WHERE total_cost = 0; -- zero-cost edge case (EDGE-INSPECT)
-- vw_Maintenance_Schedule
SELECT TOP (5) site, plant_no, EQUIP_NO, schedule_id, freq_days, next_due
FROM dbo.vw_Maintenance_Schedule
WHERE site = N'SA001';
-- vw_Failure_Codes
SELECT TOP (5) site, plant_no, failure_code, failure_class
FROM dbo.vw_Failure_Codes
WHERE site = N'SA001' AND plant_no = N'SA001-0100-PMP-0001';
-- vw_CrossAsset_Peer_Metrics
SELECT TOP (5) site, plant_no, EQUIP_NO, EQPT_TYPE, peer_count, avg_cost, mtbf_days
FROM dbo.vw_CrossAsset_Peer_Metrics
WHERE EQPT_TYPE = N'PMP';
-- vw_Asset_Attributes
SELECT TOP (5) site, plant_no, EQUIP_NO, attr_name, attr_value, attr_uom
FROM dbo.vw_Asset_Attributes
WHERE plant_no = N'SA001-0100-PMP-0001';
Local:
docker compose up -d --wait
sqlcmd -S localhost,1433 -U sa -P "CalabMocks!2024" -C -d edw \
-Q "SELECT work_order, EQUIP_NO, has_documents FROM dbo.vw_WorkOrder_Enriched;"
Azure (Entra token, after azd up + CI seed):
sqlcmd -S "$(azd env get-value SQL_SERVER_FQDN)" -d edw -G -C \
-Q "SELECT TOP (5) * FROM dbo.vw_WorkOrder_Enriched;"