@luquimbo/bi-superpowers 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +46 -0
- package/.claude-plugin/plugin.json +1 -1
- package/.mcp.json +4 -0
- package/AGENTS.md +34 -7
- package/README.md +139 -118
- package/bin/cli.js +45 -31
- package/bin/commands/install.js +321 -0
- package/bin/lib/microsoft-mcp.js +8 -0
- package/bin/lib/microsoft-mcp.test.js +5 -0
- package/package.json +1 -1
- package/skills/contributions/SKILL.md +1 -1
- package/skills/data-model-design/SKILL.md +1 -1
- package/skills/data-modeling/SKILL.md +82 -56
- package/skills/data-quality/SKILL.md +1 -1
- package/skills/dax/SKILL.md +74 -36
- package/skills/dax-doctor/SKILL.md +1 -1
- package/skills/dax-udf/SKILL.md +1 -1
- package/skills/deployment/SKILL.md +1 -1
- package/skills/excel-formulas/SKILL.md +1 -1
- package/skills/fabric-scripts/SKILL.md +1 -1
- package/skills/fast-standard/SKILL.md +1 -1
- package/skills/governance/SKILL.md +103 -50
- package/skills/migration-assistant/SKILL.md +1 -1
- package/skills/model-documenter/SKILL.md +1 -1
- package/skills/pbi-connect/SKILL.md +1 -1
- package/skills/power-query/SKILL.md +1 -1
- package/skills/project-kickoff/SKILL.md +1 -1
- package/skills/query-performance/SKILL.md +1 -1
- package/skills/report-design/SKILL.md +1 -1
- package/skills/report-layout/SKILL.md +1 -1
- package/skills/rls-design/SKILL.md +1 -1
- package/skills/semantic-model/SKILL.md +1 -1
- package/skills/testing-validation/SKILL.md +1 -1
- package/skills/theme-tweaker/SKILL.md +1 -1
- package/src/content/mcp-requirements.json +13 -0
- package/src/content/skills/data-modeling.md +81 -55
- package/src/content/skills/dax.md +73 -35
- package/src/content/skills/governance.md +102 -49
|
@@ -22,56 +22,60 @@ Best practices for designing semantic models in Power BI.
|
|
|
22
22
|
|
|
23
23
|
### Fact Tables
|
|
24
24
|
- Contain measurable events/transactions
|
|
25
|
+
- **Plural names** (multiple events): `Sales`, `Orders`, `Transactions`
|
|
26
|
+
- **No `Fact_` prefix** — that's developer jargon, users see this in the model
|
|
25
27
|
- Granularity: one row per event/transaction
|
|
26
|
-
- Include foreign keys to dimension tables
|
|
27
|
-
-
|
|
28
|
+
- Include foreign keys to dimension tables (hidden from report view)
|
|
29
|
+
- Hide raw numeric columns; expose them through explicit measures
|
|
28
30
|
|
|
29
31
|
```
|
|
30
|
-
|
|
31
|
-
├──
|
|
32
|
-
├── DateKey (FK)
|
|
33
|
-
├── ProductKey (FK)
|
|
34
|
-
├── CustomerKey (FK)
|
|
35
|
-
├── StoreKey (FK)
|
|
36
|
-
├── Quantity
|
|
37
|
-
├──
|
|
38
|
-
└──
|
|
32
|
+
Sales ← plural, business name, no prefix
|
|
33
|
+
├── SalesKey (PK, hidden)
|
|
34
|
+
├── DateKey (FK, hidden)
|
|
35
|
+
├── ProductKey (FK, hidden)
|
|
36
|
+
├── CustomerKey (FK, hidden)
|
|
37
|
+
├── StoreKey (FK, hidden)
|
|
38
|
+
├── Quantity (hidden, exposed via [Quantity Sold] measure)
|
|
39
|
+
├── Unit Price (hidden, used in measures)
|
|
40
|
+
└── Sales Amount (hidden, used in [Sales] measure)
|
|
39
41
|
```
|
|
40
42
|
|
|
41
43
|
### Dimension Tables
|
|
42
44
|
- Contain descriptive attributes
|
|
43
|
-
-
|
|
45
|
+
- **Singular names** (describes one entity): `Customer`, `Product`, `Date`
|
|
46
|
+
- **No `Dim_` prefix** — users see this name
|
|
47
|
+
- Include surrogate key (integer PK, hidden)
|
|
44
48
|
- Denormalized for simplicity
|
|
45
49
|
- Include business key for lookups
|
|
46
50
|
|
|
47
51
|
```
|
|
48
|
-
|
|
49
|
-
├── ProductKey (PK, surrogate)
|
|
50
|
-
├──
|
|
51
|
-
├──
|
|
52
|
+
Product ← singular, business name, no prefix
|
|
53
|
+
├── ProductKey (PK, surrogate, hidden)
|
|
54
|
+
├── Product ID (business key, visible)
|
|
55
|
+
├── Product Name
|
|
52
56
|
├── Category
|
|
53
57
|
├── Subcategory
|
|
54
58
|
├── Brand
|
|
55
|
-
└──
|
|
59
|
+
└── Unit Cost
|
|
56
60
|
```
|
|
57
61
|
|
|
58
62
|
### Date Dimension
|
|
59
|
-
Essential for time intelligence. Mark as Date Table in Power BI.
|
|
63
|
+
Essential for time intelligence. Mark as Date Table in Power BI. Singular: `Date`, never `Dates` or `Dim_Date`.
|
|
60
64
|
|
|
61
65
|
```
|
|
62
|
-
|
|
63
|
-
├── DateKey (PK, YYYYMMDD integer)
|
|
66
|
+
Date ← singular, no prefix
|
|
67
|
+
├── DateKey (PK, YYYYMMDD integer, hidden)
|
|
64
68
|
├── Date (actual date)
|
|
65
69
|
├── Year
|
|
66
70
|
├── Quarter
|
|
67
71
|
├── Month
|
|
68
|
-
├──
|
|
72
|
+
├── Month Name
|
|
69
73
|
├── Week
|
|
70
|
-
├──
|
|
71
|
-
├──
|
|
72
|
-
├──
|
|
73
|
-
├──
|
|
74
|
-
└──
|
|
74
|
+
├── Day of Week
|
|
75
|
+
├── Day Name
|
|
76
|
+
├── Is Weekend
|
|
77
|
+
├── Is Holiday
|
|
78
|
+
└── Fiscal Year (if different from calendar)
|
|
75
79
|
```
|
|
76
80
|
|
|
77
81
|
## Relationship Best Practices
|
|
@@ -117,55 +121,62 @@ Option 2: Duplicate dimension tables (clearer but redundant)
|
|
|
117
121
|
|
|
118
122
|
## Naming Conventions
|
|
119
123
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
|
123
|
-
|
|
124
|
-
|
|
|
125
|
-
|
|
|
126
|
-
|
|
|
124
|
+
**Core principle:** user-visible names speak business language, technical elements stay hidden. No `dim`, `fact`, `tbl` prefixes — those leak SQL jargon into the user experience.
|
|
125
|
+
|
|
126
|
+
| Element | Convention | Example | Anti-Pattern |
|
|
127
|
+
|---------|------------|---------|--------------|
|
|
128
|
+
| Fact tables | Plural, business name | `Sales`, `Orders`, `Transactions` | `Fact_Sales`, `fct_sales` |
|
|
129
|
+
| Dimension tables | Singular, business name | `Customer`, `Product`, `Date` | `Dim_Customer`, `dim_customers` |
|
|
130
|
+
| Bridge tables | `Bridge` prefix | `Bridge Customer Region` | `BridgeCustomerRegion` |
|
|
131
|
+
| Foreign keys | `[Entity]Key`, hidden | `ProductKey`, `CustomerKey` | `prod_id`, `FK_Product` |
|
|
132
|
+
| Surrogate PK | `[Entity]Key`, hidden | `CustomerKey` | `ID`, `pk_customer` |
|
|
133
|
+
| Business key | `[Entity] ID` or `Code` | `Customer ID`, `Product Code` | `BusinessKey` |
|
|
134
|
+
| Attribute columns | Natural language | `First Name`, `Order Date` | `FrstNm`, `OrdDt` |
|
|
135
|
+
| Measures | Auto-explicative, no prefix | `Sales`, `Margin %`, `Sales YTD` | `Total Sales`, `Sum of Sales` |
|
|
136
|
+
|
|
137
|
+
See `/governance` for the complete naming guide.
|
|
127
138
|
|
|
128
139
|
## Common Patterns
|
|
129
140
|
|
|
130
141
|
### Many-to-Many with Bridge Table
|
|
131
142
|
```
|
|
132
|
-
|
|
143
|
+
Customer ──(1:*)── Bridge Customer Product ──(*:1)── Product
|
|
133
144
|
```
|
|
134
145
|
|
|
135
146
|
### Slowly Changing Dimensions (SCD)
|
|
136
147
|
```
|
|
137
148
|
Type 1: Overwrite (no history)
|
|
138
149
|
Type 2: Add new row with version tracking
|
|
139
|
-
-
|
|
150
|
+
- Start Date, End Date, Is Current flag
|
|
140
151
|
|
|
141
|
-
|
|
142
|
-
├── CustomerKey (surrogate, unique per version)
|
|
143
|
-
├──
|
|
144
|
-
├──
|
|
152
|
+
Customer (SCD2)
|
|
153
|
+
├── CustomerKey (surrogate, unique per version, hidden)
|
|
154
|
+
├── Customer ID (business key)
|
|
155
|
+
├── Customer Name
|
|
145
156
|
├── Address
|
|
146
|
-
├──
|
|
147
|
-
├──
|
|
148
|
-
└──
|
|
157
|
+
├── Start Date
|
|
158
|
+
├── End Date
|
|
159
|
+
└── Is Current
|
|
149
160
|
```
|
|
150
161
|
|
|
151
162
|
### Junk Dimension (Low-Cardinality Flags)
|
|
152
163
|
Combine multiple low-cardinality attributes:
|
|
153
164
|
```
|
|
154
|
-
|
|
155
|
-
├── OrderFlagKey
|
|
156
|
-
├──
|
|
157
|
-
├──
|
|
158
|
-
├──
|
|
159
|
-
└──
|
|
165
|
+
Order Flags
|
|
166
|
+
├── OrderFlagKey (hidden)
|
|
167
|
+
├── Is Rush
|
|
168
|
+
├── Is Gift
|
|
169
|
+
├── Is Online
|
|
170
|
+
└── Payment Type
|
|
160
171
|
```
|
|
161
172
|
|
|
162
173
|
### Degenerate Dimension
|
|
163
174
|
Attributes stored in fact table (no separate dimension):
|
|
164
175
|
```
|
|
165
|
-
|
|
176
|
+
Sales
|
|
166
177
|
├── ...
|
|
167
|
-
├──
|
|
168
|
-
├──
|
|
178
|
+
├── Invoice Number (degenerate dimension)
|
|
179
|
+
├── Order Number (degenerate dimension)
|
|
169
180
|
└── ...
|
|
170
181
|
```
|
|
171
182
|
|
|
@@ -191,9 +202,12 @@ Fact_Sales
|
|
|
191
202
|
|
|
192
203
|
## Model Validation Checklist
|
|
193
204
|
|
|
205
|
+
- [ ] No technical prefixes (`Dim_`, `Fact_`, `tbl_`)
|
|
206
|
+
- [ ] Dimensions are singular (`Customer`), facts are plural (`Sales`)
|
|
207
|
+
- [ ] Foreign keys hidden from report view
|
|
208
|
+
- [ ] Sumable source columns hidden (only explicit measures visible)
|
|
194
209
|
- [ ] All relationships are single-direction (except where required)
|
|
195
210
|
- [ ] Date table marked as Date Table
|
|
196
|
-
- [ ] Foreign keys hidden from report view
|
|
197
211
|
- [ ] No circular dependencies
|
|
198
212
|
- [ ] Measures in dedicated folder or table
|
|
199
213
|
- [ ] Appropriate data types assigned
|
|
@@ -210,14 +224,26 @@ Fact_Sales
|
|
|
210
224
|
|
|
211
225
|
### Snowflake Complexity
|
|
212
226
|
```
|
|
213
|
-
❌
|
|
214
|
-
✓ Denormalize:
|
|
227
|
+
❌ Product → Category → Department
|
|
228
|
+
✓ Denormalize: Product (includes Category, Department columns)
|
|
215
229
|
```
|
|
216
230
|
|
|
217
231
|
### Calculated Columns for Measures
|
|
218
232
|
```
|
|
219
233
|
❌ Calculated column: [Profit] = [Revenue] - [Cost]
|
|
220
|
-
✓ Measure: Profit = SUM([Revenue]) - SUM([Cost])
|
|
234
|
+
✓ Measure: Profit = SUM(Sales[Revenue]) - SUM(Sales[Cost])
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Technical Prefixes on User-Visible Tables
|
|
238
|
+
```
|
|
239
|
+
❌ Fact_Sales, Dim_Customer (developer jargon leaks to users)
|
|
240
|
+
✓ Sales, Customer (business names)
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Visible Sumable Columns
|
|
244
|
+
```
|
|
245
|
+
❌ Quantity column visible — users drag it into visuals as Sum/Average
|
|
246
|
+
✓ Hide Quantity, expose explicit measure: Quantity Sold = SUM(Sales[Quantity])
|
|
221
247
|
```
|
|
222
248
|
|
|
223
249
|
### Missing Date Dimension
|
|
@@ -15,10 +15,13 @@ You are a **DAX Expert** specializing in efficient measure design, context manip
|
|
|
15
15
|
|
|
16
16
|
## MANDATORY RULES
|
|
17
17
|
1. **USE VARIABLES.** Always use VAR/RETURN for any calculation used more than once.
|
|
18
|
-
2. **
|
|
19
|
-
3. **
|
|
20
|
-
4.
|
|
21
|
-
5. **
|
|
18
|
+
2. **FINAL VARIABLE IS `Result`.** Every measure with VAR/RETURN ends with `VAR Result = ... RETURN Result` (SQLBI convention).
|
|
19
|
+
3. **PASCALCASE FOR VARIABLES.** No underscore prefix. `TotalSales`, not `_TotalSales`.
|
|
20
|
+
4. **`@` PREFIX FOR TEMPORARY COLUMNS.** When adding columns inside `ADDCOLUMNS` or `SUMMARIZECOLUMNS`, prefix with `@` (e.g., `@Rank`, `@Sales`).
|
|
21
|
+
5. **FILTER ON DIMENSIONS.** Never filter directly on fact tables when a dimension exists.
|
|
22
|
+
6. **SAFE DIVISION.** Always use DIVIDE() instead of / operator.
|
|
23
|
+
7. **EXPLAIN CONTEXT.** Help users understand filter vs row context.
|
|
24
|
+
8. **PERFORMANCE FIRST.** Suggest optimizations proactively.
|
|
22
25
|
|
|
23
26
|
---
|
|
24
27
|
|
|
@@ -27,14 +30,26 @@ Best practices for writing and optimizing DAX in Power BI.
|
|
|
27
30
|
|
|
28
31
|
## Naming Conventions
|
|
29
32
|
|
|
33
|
+
Conventions follow the SQLBI Style Guide. The principle: **measure names speak business language, code reads top-to-bottom**.
|
|
34
|
+
|
|
30
35
|
| Element | Convention | Example |
|
|
31
36
|
|---------|------------|---------|
|
|
32
|
-
| Measures |
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
37
|
+
| Measures | Auto-explicative, no prefix | `Sales`, `Margin %`, `Sales YTD` |
|
|
38
|
+
| Time intelligence suffix | Space + period | `Sales YTD`, `Sales PY`, `Sales MTD` |
|
|
39
|
+
| Variations | Space + variation + `%` | `Sales YoY %`, `Sales MoM %` |
|
|
40
|
+
| Ratios | Space + `%` | `Margin %`, `Conversion %` |
|
|
41
|
+
| Calculated Columns | Natural name | `Full Date`, `Product Category` |
|
|
42
|
+
| Variables | PascalCase, no prefix | `TotalSales`, `FilteredTable`, `CurrentDate` |
|
|
43
|
+
| Final variable | Always `Result` | `VAR Result = ...` `RETURN Result` |
|
|
44
|
+
| Temporary columns | Prefix `@` | `@Rank`, `@Sales`, `@Delta` |
|
|
45
|
+
| Measure Tables | Prefix `_`, hidden | `_Measures`, `_KPIs` |
|
|
46
|
+
| Debug/Test Measures | `_` prefix, hidden | `_Debug Row Count` |
|
|
47
|
+
|
|
48
|
+
**Anti-patterns:**
|
|
49
|
+
- `Total Sales`, `Sum of Sales` (redundant, the aggregation is implied)
|
|
50
|
+
- `_Result`, `_FilteredTable` (no underscore prefix on variables)
|
|
51
|
+
- `YTD_Sales`, `Sales_YTD` (use space-separated suffix)
|
|
52
|
+
- `MarginPct`, `MarginRatio` (use `Margin %` instead)
|
|
38
53
|
|
|
39
54
|
---
|
|
40
55
|
|
|
@@ -100,25 +115,47 @@ SumViaMeasure = SUMX(Products, [TotalSales]) // [TotalSales] filters to each pr
|
|
|
100
115
|
- Intermediate calculations
|
|
101
116
|
- Debugging complex measures
|
|
102
117
|
|
|
118
|
+
**Conventions (SQLBI Style Guide):**
|
|
119
|
+
- **PascalCase** without underscore prefix
|
|
120
|
+
- **Final variable always `Result`** — clearly marks the measure's output
|
|
121
|
+
- **Temporary columns prefixed with `@`** to distinguish from model columns
|
|
122
|
+
|
|
103
123
|
```dax
|
|
104
|
-
-- Good:
|
|
105
|
-
|
|
106
|
-
VAR
|
|
107
|
-
VAR
|
|
124
|
+
-- Good: PascalCase vars, Result final var
|
|
125
|
+
Sales YoY % =
|
|
126
|
+
VAR CurrentSales = [Sales]
|
|
127
|
+
VAR PriorYearSales =
|
|
108
128
|
CALCULATE(
|
|
109
|
-
[
|
|
129
|
+
[Sales],
|
|
110
130
|
SAMEPERIODLASTYEAR('Date'[Date])
|
|
111
131
|
)
|
|
112
|
-
VAR
|
|
113
|
-
|
|
114
|
-
|
|
132
|
+
VAR Delta = CurrentSales - PriorYearSales
|
|
133
|
+
VAR Result = DIVIDE(Delta, PriorYearSales)
|
|
134
|
+
RETURN Result
|
|
135
|
+
|
|
136
|
+
-- Good: Temporary column with @ prefix
|
|
137
|
+
Top 10 Products Sales =
|
|
138
|
+
VAR RankedProducts =
|
|
139
|
+
ADDCOLUMNS(
|
|
140
|
+
VALUES(Product[Product Name]),
|
|
141
|
+
"@Rank", RANKX(VALUES(Product[Product Name]), [Sales], , DESC)
|
|
142
|
+
)
|
|
143
|
+
VAR Top10 = FILTER(RankedProducts, [@Rank] <= 10)
|
|
144
|
+
VAR Result = SUMX(Top10, [Sales])
|
|
145
|
+
RETURN Result
|
|
115
146
|
|
|
116
147
|
-- Bad: Repeated calculations (slower, harder to read)
|
|
117
|
-
|
|
148
|
+
Sales YoY Bad =
|
|
118
149
|
DIVIDE(
|
|
119
|
-
[
|
|
120
|
-
CALCULATE([
|
|
150
|
+
[Sales] - CALCULATE([Sales], SAMEPERIODLASTYEAR('Date'[Date])),
|
|
151
|
+
CALCULATE([Sales], SAMEPERIODLASTYEAR('Date'[Date]))
|
|
121
152
|
)
|
|
153
|
+
|
|
154
|
+
-- Bad: Underscore prefix on variables (old convention)
|
|
155
|
+
Sales YoY Old =
|
|
156
|
+
VAR _CurrentSales = [Sales]
|
|
157
|
+
VAR _PriorYearSales = CALCULATE([Sales], SAMEPERIODLASTYEAR('Date'[Date]))
|
|
158
|
+
RETURN DIVIDE(_CurrentSales - _PriorYearSales, _PriorYearSales)
|
|
122
159
|
```
|
|
123
160
|
|
|
124
161
|
### Safe Division
|
|
@@ -627,29 +664,30 @@ Running_Total = CALCULATE(...) -- Use measures
|
|
|
627
664
|
## Formatting Standards
|
|
628
665
|
|
|
629
666
|
```dax
|
|
630
|
-
-- Simple measure (one line)
|
|
631
|
-
|
|
667
|
+
-- Simple measure (one line, no VAR/RETURN needed)
|
|
668
|
+
Sales = SUM(Sales[Amount])
|
|
632
669
|
|
|
633
670
|
-- Medium complexity (few lines)
|
|
634
|
-
Margin = DIVIDE([Profit], [Revenue], 0)
|
|
671
|
+
Margin % = DIVIDE([Profit], [Revenue], 0)
|
|
635
672
|
|
|
636
|
-
-- Complex measure (structured with comments)
|
|
637
|
-
|
|
638
|
-
// Purpose:
|
|
639
|
-
VAR
|
|
640
|
-
VAR
|
|
673
|
+
-- Complex measure (structured with comments, Result as final var)
|
|
674
|
+
Sales YoY % =
|
|
675
|
+
// Purpose: Year-over-year change with handling for missing prior year
|
|
676
|
+
VAR CurrentSales = [Sales]
|
|
677
|
+
VAR PriorYearSales =
|
|
641
678
|
CALCULATE(
|
|
642
|
-
[
|
|
679
|
+
[Sales],
|
|
643
680
|
SAMEPERIODLASTYEAR('Date'[Date])
|
|
644
681
|
)
|
|
645
|
-
VAR
|
|
646
|
-
VAR
|
|
647
|
-
|
|
682
|
+
VAR Delta = CurrentSales - PriorYearSales
|
|
683
|
+
VAR HasPriorYear = NOT(ISBLANK(PriorYearSales))
|
|
684
|
+
VAR Result =
|
|
648
685
|
IF(
|
|
649
|
-
|
|
650
|
-
DIVIDE(
|
|
686
|
+
HasPriorYear,
|
|
687
|
+
DIVIDE(Delta, PriorYearSales),
|
|
651
688
|
BLANK()
|
|
652
689
|
)
|
|
690
|
+
RETURN Result
|
|
653
691
|
```
|
|
654
692
|
|
|
655
693
|
---
|
|
@@ -22,57 +22,100 @@ You are a **BI Governance Specialist** who establishes and enforces naming conve
|
|
|
22
22
|
|
|
23
23
|
## Naming Conventions
|
|
24
24
|
|
|
25
|
+
Conventions follow SQLBI Style Guide, Chris Webb, and Microsoft's semantic model best practices. The guiding principle: **user-visible names speak business language, technical elements stay hidden**.
|
|
26
|
+
|
|
25
27
|
### Tables
|
|
26
28
|
|
|
29
|
+
**Rules:**
|
|
30
|
+
1. **No technical prefixes.** Never use `dim`, `fact`, `tbl`, `t_`. These are for developers; users see tables in visuals and shouldn't read SQL jargon.
|
|
31
|
+
2. **Dimensions singular, facts plural.** A dimension describes one entity (`Customer`, `Product`, `Date`). A fact contains many events (`Sales`, `Orders`, `Transactions`).
|
|
32
|
+
3. **Spaces allowed for user-visible tables.** Use natural language: `Purchase Orders`, not `PurchaseOrders` or `PO_Header`.
|
|
33
|
+
4. **Business names, not source names.** Match the language users speak, not the database schema.
|
|
34
|
+
|
|
27
35
|
| Type | Convention | Example | Anti-Pattern |
|
|
28
36
|
|------|-----------|---------|-------------|
|
|
29
|
-
| Dimension | Singular
|
|
30
|
-
| Fact |
|
|
31
|
-
| Bridge | Prefix with `Bridge` | `
|
|
32
|
-
| Measure table | Prefix `_` | `_Measures`, `_KPIs` | `Measures Table
|
|
33
|
-
| Config/Utility | Prefix `_` | `_Parameters`, `_DateConfig` | `Parameters` |
|
|
34
|
-
| Calculated table | Document clearly | `CalendarTable` (with comment) | Unmarked calculated tables |
|
|
37
|
+
| Dimension | Singular, business name | `Customer`, `Product`, `Date` | `Customers`, `DimCustomer`, `tbl_Customer` |
|
|
38
|
+
| Fact | Plural, business name | `Sales`, `Orders`, `Transactions` | `FactSales`, `fct_sales`, `Sale` |
|
|
39
|
+
| Bridge | Prefix with `Bridge` | `Bridge Customer Region` | `CustomerRegion_Bridge` |
|
|
40
|
+
| Measure table | Prefix `_` (hidden) | `_Measures`, `_KPIs` | `Measures Table` |
|
|
41
|
+
| Config/Utility | Prefix `_` (hidden) | `_Parameters`, `_DateConfig` | `Parameters` |
|
|
35
42
|
|
|
36
43
|
### Columns
|
|
37
44
|
|
|
45
|
+
**Rules:**
|
|
46
|
+
1. **Descriptive and readable.** `Order Date`, not `OrdDt`. Spaces allowed.
|
|
47
|
+
2. **Foreign keys hidden with special marker.** Use `CustomerKey` suffix or `_Customer` prefix to sort them together. **Always hide from report view.**
|
|
48
|
+
3. **No abbreviations** except universal ones (`YTD`, `PY`, `MTD`, `QTD`, `YoY`). Avoid `Qty`, `Amt`, `Vta`, `Cant`.
|
|
49
|
+
4. **Consistency.** If you use spaces in one table, use them everywhere.
|
|
50
|
+
|
|
38
51
|
| Type | Convention | Example |
|
|
39
52
|
|------|-----------|---------|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
| Attribute |
|
|
43
|
-
| Flag/Boolean | `Is[Condition]` | `
|
|
44
|
-
| Amount/Currency |
|
|
45
|
-
| Count |
|
|
46
|
-
| Date |
|
|
53
|
+
| Primary key (surrogate) | `[Table]Key`, hidden | `CustomerKey`, `ProductKey` |
|
|
54
|
+
| Primary key (business) | `[Entity] ID` or `[Entity] Code` | `Customer ID`, `Product Code` |
|
|
55
|
+
| Attribute | Natural language | `First Name`, `Order Date`, `Product Category` |
|
|
56
|
+
| Flag/Boolean | `Is [Condition]` or `Has [Condition]` | `Is Active`, `Has Discount` |
|
|
57
|
+
| Amount/Currency | Include unit in name | `Sales Amount`, `Discount Amount` |
|
|
58
|
+
| Count | Include `Count` suffix | `Order Count`, `Item Count` |
|
|
59
|
+
| Date | Include `Date` suffix | `Order Date`, `Ship Date` |
|
|
47
60
|
|
|
48
61
|
### Measures
|
|
49
62
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
**Rules:**
|
|
64
|
+
1. **Auto-explicative names.** The name alone should tell you what it calculates. No documentation required.
|
|
65
|
+
2. **No redundant prefixes.** Use `Sales`, not `Total Sales`, `Sum of Sales`, or `Amount of Sales`. The aggregation is implied.
|
|
66
|
+
3. **Space-separated suffixes for time intelligence.** `Sales YTD`, `Sales PY`, `Sales YoY %`, `Sales MTD`.
|
|
67
|
+
4. **Space-separated suffixes for ratios and percentages.** `Margin %`, `Growth %`, `Conversion Rate`.
|
|
68
|
+
5. **Never prefix with the table name.** Just `Sales`, not `Transactions[Sales]` in the visible name.
|
|
69
|
+
6. **Explicit measures, hidden source columns.** Create DAX measures for every numeric calculation. Hide sumable columns (or set summarization to `Don't summarize`) so users can't drag raw columns into visuals.
|
|
70
|
+
7. **Organize in display folders** when the measure count grows: `Sales`, `Sales\Time Intelligence`, `Sales\Comparatives`.
|
|
71
|
+
|
|
72
|
+
| Type | Convention | Example | Anti-Pattern |
|
|
73
|
+
|------|-----------|---------|-------------|
|
|
74
|
+
| Base aggregation | Noun, no prefix | `Sales`, `Customers`, `Units Sold` | `Total Sales`, `Sum of Sales`, `Sales Amount Total` |
|
|
75
|
+
| Time intelligence | Noun + period suffix | `Sales YTD`, `Sales PY`, `Sales MTD` | `YTD_Sales`, `Sales_PriorYear` |
|
|
76
|
+
| Variation | Noun + variation + `%` | `Sales YoY %`, `Sales MoM %` | `YoY_Pct_Sales` |
|
|
77
|
+
| Ratio/Percentage | Noun + ` %` | `Margin %`, `Conversion %` | `MarginPct`, `ConversionRatio` |
|
|
78
|
+
| KPI vs target | Noun + vs Target | `Sales vs Target`, `Sales vs Budget %` | `KPI_Sales` |
|
|
79
|
+
| Ranking | `Rank: ` prefix or `Rank` suffix | `Rank: Top Products`, `Product Rank` | `RANK_Products` |
|
|
80
|
+
| Debug/Test | `_` prefix (hidden) | `_Debug Row Count` | `Debug: Row Count` (visible) |
|
|
59
81
|
|
|
60
82
|
### Variables (DAX)
|
|
61
83
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
84
|
+
**Rules:**
|
|
85
|
+
1. **PascalCase.** No underscore prefix. `TotalSales`, `AverageMargin`, `FilteredTable`.
|
|
86
|
+
2. **Final variable always named `Result`.** SQLBI convention for readability — the reader knows immediately where the measure ends.
|
|
87
|
+
3. **Temporary columns prefixed with `@`.** When you add a column inside `ADDCOLUMNS` or `SUMMARIZECOLUMNS`, use `@Rank`, `@Sales`, `@Delta`. Distinguishes them from model columns.
|
|
88
|
+
|
|
89
|
+
```dax
|
|
90
|
+
// Good: PascalCase vars, Result final var, @ for temp columns
|
|
91
|
+
Sales YoY % =
|
|
92
|
+
VAR CurrentSales = [Sales]
|
|
93
|
+
VAR PriorYearSales =
|
|
94
|
+
CALCULATE([Sales], SAMEPERIODLASTYEAR('Date'[Date]))
|
|
95
|
+
VAR Delta = CurrentSales - PriorYearSales
|
|
96
|
+
VAR Result = DIVIDE(Delta, PriorYearSales)
|
|
97
|
+
RETURN Result
|
|
98
|
+
|
|
99
|
+
// Temporary column example
|
|
100
|
+
Top Products =
|
|
101
|
+
VAR RankedProducts =
|
|
102
|
+
ADDCOLUMNS(
|
|
103
|
+
VALUES(Product[Product Name]),
|
|
104
|
+
"@Rank", RANKX(VALUES(Product[Product Name]), [Sales], , DESC)
|
|
105
|
+
)
|
|
106
|
+
VAR Result =
|
|
107
|
+
FILTER(RankedProducts, [@Rank] <= 10)
|
|
108
|
+
RETURN Result
|
|
109
|
+
```
|
|
67
110
|
|
|
68
111
|
### Power Query
|
|
69
112
|
|
|
70
113
|
| Element | Convention | Example |
|
|
71
114
|
|---------|-----------|---------|
|
|
72
|
-
| Query name |
|
|
73
|
-
| Staging query | Prefix
|
|
74
|
-
| Helper function | Prefix
|
|
75
|
-
| Parameter | Prefix
|
|
115
|
+
| Query name | Matches destination table | `Customer`, `Sales` |
|
|
116
|
+
| Staging query | Prefix `stg `, hidden | `stg Customer Raw` |
|
|
117
|
+
| Helper function | Prefix `fn` | `fnCleanText`, `fnGetFiscalYear` |
|
|
118
|
+
| Parameter | Prefix `prm` | `prmServerName`, `prmStartDate` |
|
|
76
119
|
| Step names | Descriptive, PascalCase | `FilteredActiveRows`, `RenamedColumns` |
|
|
77
120
|
|
|
78
121
|
---
|
|
@@ -81,26 +124,28 @@ You are a **BI Governance Specialist** who establishes and enforces naming conve
|
|
|
81
124
|
|
|
82
125
|
### Recommended Folder Structure
|
|
83
126
|
|
|
127
|
+
Display folders separate measures with `\` (backslash). Use them as soon as you have more than ~10 measures.
|
|
128
|
+
|
|
84
129
|
```
|
|
85
130
|
_Measures/
|
|
86
|
-
├── 📁 Core
|
|
87
|
-
│ ├──
|
|
88
|
-
│ ├──
|
|
89
|
-
│ └──
|
|
131
|
+
├── 📁 Core
|
|
132
|
+
│ ├── Sales
|
|
133
|
+
│ ├── Cost
|
|
134
|
+
│ └── Gross Margin
|
|
90
135
|
├── 📁 Time Intelligence
|
|
91
|
-
│ ├──
|
|
92
|
-
│ ├──
|
|
93
|
-
│ └──
|
|
94
|
-
├── 📁
|
|
95
|
-
│ ├──
|
|
96
|
-
│ ├──
|
|
97
|
-
│ └──
|
|
136
|
+
│ ├── Sales YTD
|
|
137
|
+
│ ├── Sales PY
|
|
138
|
+
│ └── Sales YoY %
|
|
139
|
+
├── 📁 Targets
|
|
140
|
+
│ ├── Sales Target
|
|
141
|
+
│ ├── Sales vs Target
|
|
142
|
+
│ └── Sales vs Target %
|
|
98
143
|
├── 📁 Rankings
|
|
99
|
-
│ ├──
|
|
100
|
-
│ └──
|
|
144
|
+
│ ├── Rank: Top Products
|
|
145
|
+
│ └── Rank: Top Customers
|
|
101
146
|
└── 📁 _Debug (hidden in production)
|
|
102
|
-
├──
|
|
103
|
-
└──
|
|
147
|
+
├── _Debug Row Count
|
|
148
|
+
└── _Test Sales
|
|
104
149
|
```
|
|
105
150
|
|
|
106
151
|
---
|
|
@@ -139,11 +184,13 @@ Every model should have:
|
|
|
139
184
|
### Measure Documentation Template
|
|
140
185
|
|
|
141
186
|
```dax
|
|
142
|
-
// Description: Calculates
|
|
187
|
+
// Description: Calculates sales for the current year to date
|
|
143
188
|
// Business rule: Includes all confirmed and shipped orders, excludes cancelled
|
|
144
189
|
// Owner: [team/person]
|
|
145
190
|
// Last modified: [date]
|
|
146
|
-
|
|
191
|
+
Sales YTD =
|
|
192
|
+
VAR Result = TOTALYTD([Sales], 'Date'[Date])
|
|
193
|
+
RETURN Result
|
|
147
194
|
```
|
|
148
195
|
|
|
149
196
|
---
|
|
@@ -165,11 +212,17 @@ TotalSales_YTD = TOTALYTD([TotalSales], 'Date'[Date])
|
|
|
165
212
|
### Code Review Checklist
|
|
166
213
|
|
|
167
214
|
- [ ] Measures use VAR/RETURN for repeated calculations
|
|
215
|
+
- [ ] Final variable in every measure is named `Result`
|
|
216
|
+
- [ ] Variables use PascalCase (no underscore prefix)
|
|
217
|
+
- [ ] Temporary columns use `@` prefix (e.g., `@Rank`)
|
|
168
218
|
- [ ] DIVIDE() used instead of / operator
|
|
169
219
|
- [ ] No FILTER on fact tables (use dimension filters)
|
|
170
|
-
- [ ] Variables follow naming conventions
|
|
171
220
|
- [ ] Complex measures have comments
|
|
172
221
|
- [ ] No hardcoded values (use parameters)
|
|
222
|
+
- [ ] No technical prefixes on tables (`Dim_`, `Fact_`)
|
|
223
|
+
- [ ] Foreign keys hidden from report view
|
|
224
|
+
- [ ] Sumable source columns hidden (only explicit measures visible)
|
|
225
|
+
- [ ] Measure names are auto-explicative (no `Total`, `Sum of`)
|
|
173
226
|
|
|
174
227
|
---
|
|
175
228
|
|