@luquimbo/bi-superpowers 1.0.0
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/plugin.json +8 -0
- package/.mcp.json +25 -0
- package/AGENTS.md +244 -0
- package/CHANGELOG.md +265 -0
- package/LICENSE +21 -0
- package/README.md +211 -0
- package/bin/build-plugin.js +30 -0
- package/bin/cli.js +1064 -0
- package/bin/commands/add.js +533 -0
- package/bin/commands/add.test.js +77 -0
- package/bin/commands/build-desktop.js +166 -0
- package/bin/commands/changelog.js +443 -0
- package/bin/commands/diff.js +325 -0
- package/bin/commands/lint.js +419 -0
- package/bin/commands/lint.test.js +103 -0
- package/bin/commands/mcp-setup.js +246 -0
- package/bin/commands/pull.js +287 -0
- package/bin/commands/pull.test.js +36 -0
- package/bin/commands/push.js +231 -0
- package/bin/commands/push.test.js +14 -0
- package/bin/commands/search.js +344 -0
- package/bin/commands/search.test.js +115 -0
- package/bin/commands/setup.js +545 -0
- package/bin/commands/setup.test.js +46 -0
- package/bin/commands/sync-profile.js +405 -0
- package/bin/commands/sync-profile.test.js +14 -0
- package/bin/commands/sync-source.js +418 -0
- package/bin/commands/sync-source.test.js +14 -0
- package/bin/commands/watch.js +206 -0
- package/bin/lib/generators/claude-plugin.js +266 -0
- package/bin/lib/generators/claude-plugin.test.js +110 -0
- package/bin/lib/generators/index.js +116 -0
- package/bin/lib/generators/shared.js +282 -0
- package/bin/lib/licensing/index.js +35 -0
- package/bin/lib/licensing/storage.js +364 -0
- package/bin/lib/licensing/storage.test.js +55 -0
- package/bin/lib/licensing/validator.js +213 -0
- package/bin/lib/licensing/validator.test.js +137 -0
- package/bin/lib/microsoft-mcp.js +176 -0
- package/bin/lib/microsoft-mcp.test.js +106 -0
- package/bin/lib/skills.js +84 -0
- package/bin/mcp/powerbi-modeling-launcher.js +38 -0
- package/bin/postinstall.js +44 -0
- package/bin/utils/errors.js +159 -0
- package/bin/utils/git.js +298 -0
- package/bin/utils/logger.js +142 -0
- package/bin/utils/mcp-detect.js +274 -0
- package/bin/utils/mcp-detect.test.js +105 -0
- package/bin/utils/pbix.js +305 -0
- package/bin/utils/pbix.test.js +37 -0
- package/bin/utils/profiles.js +312 -0
- package/bin/utils/projects.js +168 -0
- package/bin/utils/readline.js +206 -0
- package/bin/utils/readline.test.js +47 -0
- package/bin/utils/tui.js +314 -0
- package/bin/utils/tui.test.js +127 -0
- package/commands/contributions.md +265 -0
- package/commands/data-model-design.md +468 -0
- package/commands/dax-doctor.md +248 -0
- package/commands/fabric-scripts.md +452 -0
- package/commands/migration-assistant.md +290 -0
- package/commands/model-documenter.md +242 -0
- package/commands/pbi-connect.md +239 -0
- package/commands/project-kickoff.md +905 -0
- package/commands/report-layout.md +296 -0
- package/commands/rls-design.md +533 -0
- package/commands/theme-tweaker.md +624 -0
- package/config.example.json +23 -0
- package/config.json +23 -0
- package/desktop-extension/manifest.json +37 -0
- package/desktop-extension/package.json +10 -0
- package/desktop-extension/server.js +95 -0
- package/docs/openrouter-free-models.md +92 -0
- package/library/examples/README.md +151 -0
- package/library/examples/finance-reporting/README.md +351 -0
- package/library/examples/finance-reporting/data-model.md +267 -0
- package/library/examples/finance-reporting/measures.dax +557 -0
- package/library/examples/hr-analytics/README.md +371 -0
- package/library/examples/hr-analytics/data-model.md +315 -0
- package/library/examples/hr-analytics/measures.dax +460 -0
- package/library/examples/marketing-analytics/README.md +37 -0
- package/library/examples/marketing-analytics/data-model.md +62 -0
- package/library/examples/marketing-analytics/measures.dax +110 -0
- package/library/examples/retail-analytics/README.md +439 -0
- package/library/examples/retail-analytics/data-model.md +288 -0
- package/library/examples/retail-analytics/measures.dax +481 -0
- package/library/examples/supply-chain/README.md +37 -0
- package/library/examples/supply-chain/data-model.md +69 -0
- package/library/examples/supply-chain/measures.dax +77 -0
- package/library/examples/udf-library/README.md +228 -0
- package/library/examples/udf-library/functions.dax +571 -0
- package/library/snippets/dax/README.md +292 -0
- package/library/snippets/dax/business-domains.md +576 -0
- package/library/snippets/dax/calculate-patterns.md +276 -0
- package/library/snippets/dax/calculation-groups.md +489 -0
- package/library/snippets/dax/error-handling.md +495 -0
- package/library/snippets/dax/iterators-and-aggregations.md +474 -0
- package/library/snippets/dax/kpis-and-metrics.md +293 -0
- package/library/snippets/dax/rankings-and-topn.md +235 -0
- package/library/snippets/dax/security-patterns.md +413 -0
- package/library/snippets/dax/text-and-formatting.md +316 -0
- package/library/snippets/dax/time-intelligence.md +196 -0
- package/library/snippets/dax/user-defined-functions.md +477 -0
- package/library/snippets/dax/virtual-tables.md +546 -0
- package/library/snippets/excel-formulas/README.md +84 -0
- package/library/snippets/excel-formulas/aggregations.md +330 -0
- package/library/snippets/excel-formulas/dates-and-times.md +361 -0
- package/library/snippets/excel-formulas/dynamic-arrays.md +314 -0
- package/library/snippets/excel-formulas/lookups.md +169 -0
- package/library/snippets/excel-formulas/text-functions.md +363 -0
- package/library/snippets/governance/naming-conventions.md +97 -0
- package/library/snippets/governance/review-checklists.md +107 -0
- package/library/snippets/power-query/README.md +389 -0
- package/library/snippets/power-query/api-integration.md +707 -0
- package/library/snippets/power-query/connections.md +434 -0
- package/library/snippets/power-query/data-cleaning.md +298 -0
- package/library/snippets/power-query/error-handling.md +526 -0
- package/library/snippets/power-query/parameters.md +350 -0
- package/library/snippets/power-query/performance.md +506 -0
- package/library/snippets/power-query/transformations.md +330 -0
- package/library/snippets/report-design/accessibility.md +78 -0
- package/library/snippets/report-design/chart-selection.md +54 -0
- package/library/snippets/report-design/layout-patterns.md +87 -0
- package/library/templates/data-models/README.md +93 -0
- package/library/templates/data-models/finance-model.md +627 -0
- package/library/templates/data-models/retail-star-schema.md +473 -0
- package/library/templates/excel/README.md +83 -0
- package/library/templates/excel/budget-tracker.md +432 -0
- package/library/templates/excel/data-entry-form.md +533 -0
- package/library/templates/power-bi/README.md +72 -0
- package/library/templates/power-bi/finance-report.md +449 -0
- package/library/templates/power-bi/kpi-scorecard.md +461 -0
- package/library/templates/power-bi/sales-dashboard.md +281 -0
- package/library/themes/excel/README.md +436 -0
- package/library/themes/power-bi/README.md +271 -0
- package/library/themes/power-bi/accessible.json +307 -0
- package/library/themes/power-bi/bi-superpowers-default.json +858 -0
- package/library/themes/power-bi/corporate-blue.json +291 -0
- package/library/themes/power-bi/dark-mode.json +291 -0
- package/library/themes/power-bi/minimal.json +292 -0
- package/library/themes/power-bi/print-friendly.json +309 -0
- package/package.json +93 -0
- package/skills/contributions/SKILL.md +267 -0
- package/skills/data-model-design/SKILL.md +470 -0
- package/skills/data-modeling/SKILL.md +254 -0
- package/skills/data-quality/SKILL.md +664 -0
- package/skills/dax/SKILL.md +708 -0
- package/skills/dax-doctor/SKILL.md +250 -0
- package/skills/dax-udf/SKILL.md +489 -0
- package/skills/deployment/SKILL.md +320 -0
- package/skills/excel-formulas/SKILL.md +463 -0
- package/skills/fabric-scripts/SKILL.md +454 -0
- package/skills/fast-standard/SKILL.md +509 -0
- package/skills/governance/SKILL.md +205 -0
- package/skills/migration-assistant/SKILL.md +292 -0
- package/skills/model-documenter/SKILL.md +244 -0
- package/skills/pbi-connect/SKILL.md +241 -0
- package/skills/power-query/SKILL.md +406 -0
- package/skills/project-kickoff/SKILL.md +907 -0
- package/skills/query-performance/SKILL.md +480 -0
- package/skills/report-design/SKILL.md +207 -0
- package/skills/report-layout/SKILL.md +298 -0
- package/skills/rls-design/SKILL.md +535 -0
- package/skills/semantic-model/SKILL.md +237 -0
- package/skills/testing-validation/SKILL.md +643 -0
- package/skills/theme-tweaker/SKILL.md +626 -0
- package/src/content/base.md +237 -0
- package/src/content/mcp-requirements.json +69 -0
- package/src/content/routing.md +203 -0
- package/src/content/skills/contributions.md +259 -0
- package/src/content/skills/data-model-design.md +462 -0
- package/src/content/skills/data-modeling.md +246 -0
- package/src/content/skills/data-quality.md +656 -0
- package/src/content/skills/dax-doctor.md +242 -0
- package/src/content/skills/dax-udf.md +481 -0
- package/src/content/skills/dax.md +700 -0
- package/src/content/skills/deployment.md +312 -0
- package/src/content/skills/excel-formulas.md +455 -0
- package/src/content/skills/fabric-scripts.md +446 -0
- package/src/content/skills/fast-standard.md +501 -0
- package/src/content/skills/governance.md +197 -0
- package/src/content/skills/migration-assistant.md +284 -0
- package/src/content/skills/model-documenter.md +236 -0
- package/src/content/skills/pbi-connect.md +233 -0
- package/src/content/skills/power-query.md +398 -0
- package/src/content/skills/project-kickoff.md +899 -0
- package/src/content/skills/query-performance.md +472 -0
- package/src/content/skills/report-design.md +199 -0
- package/src/content/skills/report-layout.md +290 -0
- package/src/content/skills/rls-design.md +527 -0
- package/src/content/skills/semantic-model.md +229 -0
- package/src/content/skills/testing-validation.md +635 -0
- package/src/content/skills/theme-tweaker.md +618 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
# Error Handling and Defensive DAX
|
|
2
|
+
|
|
3
|
+
Patterns for handling errors, blanks, edge cases, and creating robust measures.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## DIVIDE Function
|
|
8
|
+
|
|
9
|
+
### Basic Safe Division
|
|
10
|
+
|
|
11
|
+
```dax
|
|
12
|
+
-- Always use DIVIDE instead of /
|
|
13
|
+
Profit_Margin =
|
|
14
|
+
DIVIDE(
|
|
15
|
+
SUM(Sales[Profit]),
|
|
16
|
+
SUM(Sales[Revenue])
|
|
17
|
+
)
|
|
18
|
+
-- Returns BLANK if denominator is 0 or blank
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Custom Alternate Result
|
|
22
|
+
|
|
23
|
+
```dax
|
|
24
|
+
-- Return 0 instead of BLANK
|
|
25
|
+
Profit_Margin_Zero =
|
|
26
|
+
DIVIDE(
|
|
27
|
+
SUM(Sales[Profit]),
|
|
28
|
+
SUM(Sales[Revenue]),
|
|
29
|
+
0 -- Return 0 when can't divide
|
|
30
|
+
)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Nested DIVIDE for Complex Calculations
|
|
34
|
+
|
|
35
|
+
```dax
|
|
36
|
+
-- Multiple divisions with safety
|
|
37
|
+
Efficiency_Ratio =
|
|
38
|
+
DIVIDE(
|
|
39
|
+
DIVIDE(SUM(Sales[Units]), SUM(Staff[Hours])),
|
|
40
|
+
DIVIDE(SUM(Budget[Units]), SUM(Budget[Hours])),
|
|
41
|
+
1 -- Return 1 (100%) if comparison not possible
|
|
42
|
+
)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## BLANK Handling
|
|
48
|
+
|
|
49
|
+
### ISBLANK / BLANK
|
|
50
|
+
|
|
51
|
+
```dax
|
|
52
|
+
-- Check for blank and substitute
|
|
53
|
+
Sales_Display =
|
|
54
|
+
IF(
|
|
55
|
+
ISBLANK(SUM(Sales[Amount])),
|
|
56
|
+
0,
|
|
57
|
+
SUM(Sales[Amount])
|
|
58
|
+
)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### COALESCE (DAX 2019+)
|
|
62
|
+
|
|
63
|
+
```dax
|
|
64
|
+
-- First non-blank value
|
|
65
|
+
Customer_Contact =
|
|
66
|
+
COALESCE(
|
|
67
|
+
[PreferredEmail],
|
|
68
|
+
[WorkEmail],
|
|
69
|
+
[PersonalEmail],
|
|
70
|
+
"No Email"
|
|
71
|
+
)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```dax
|
|
75
|
+
-- Chain of fallback values
|
|
76
|
+
Revenue_Display =
|
|
77
|
+
COALESCE(
|
|
78
|
+
[Actual Revenue],
|
|
79
|
+
[Forecast Revenue],
|
|
80
|
+
[Budget Revenue],
|
|
81
|
+
0
|
|
82
|
+
)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Handle Blank in Comparisons
|
|
86
|
+
|
|
87
|
+
```dax
|
|
88
|
+
-- Safe comparison with potential blanks
|
|
89
|
+
Has_Sales =
|
|
90
|
+
VAR _Sales = SUM(Sales[Amount])
|
|
91
|
+
RETURN
|
|
92
|
+
IF(
|
|
93
|
+
ISBLANK(_Sales) || _Sales = 0,
|
|
94
|
+
"No",
|
|
95
|
+
"Yes"
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Error Handling
|
|
102
|
+
|
|
103
|
+
### IFERROR - Catch All Errors
|
|
104
|
+
|
|
105
|
+
```dax
|
|
106
|
+
-- Handle any calculation error
|
|
107
|
+
Safe_Calculation =
|
|
108
|
+
IFERROR(
|
|
109
|
+
[ComplexMeasure] / [AnotherMeasure],
|
|
110
|
+
BLANK()
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### ISERROR - Test Before Calculate
|
|
115
|
+
|
|
116
|
+
```dax
|
|
117
|
+
-- Test for error condition
|
|
118
|
+
Error_Status =
|
|
119
|
+
IF(
|
|
120
|
+
ISERROR([RiskyCalculation]),
|
|
121
|
+
"Error in calculation",
|
|
122
|
+
FORMAT([RiskyCalculation], "#,##0")
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Error Handling in Iterators
|
|
127
|
+
|
|
128
|
+
```dax
|
|
129
|
+
-- Safe iteration with error handling
|
|
130
|
+
Total_Safe =
|
|
131
|
+
SUMX(
|
|
132
|
+
Sales,
|
|
133
|
+
IFERROR(
|
|
134
|
+
Sales[Quantity] * Sales[Price] / Sales[Discount],
|
|
135
|
+
Sales[Quantity] * Sales[Price] -- Fallback if error
|
|
136
|
+
)
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Null and Empty String Handling
|
|
143
|
+
|
|
144
|
+
### Distinguish BLANK from Empty String
|
|
145
|
+
|
|
146
|
+
```dax
|
|
147
|
+
-- Check for truly empty values
|
|
148
|
+
Has_Value =
|
|
149
|
+
VAR _Value = SELECTEDVALUE(Products[Description])
|
|
150
|
+
RETURN
|
|
151
|
+
IF(
|
|
152
|
+
ISBLANK(_Value) || _Value = "",
|
|
153
|
+
FALSE(),
|
|
154
|
+
TRUE()
|
|
155
|
+
)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Clean Text Input
|
|
159
|
+
|
|
160
|
+
```dax
|
|
161
|
+
-- Handle various empty states
|
|
162
|
+
Clean_Name =
|
|
163
|
+
VAR _Raw = SELECTEDVALUE(Customers[Name])
|
|
164
|
+
RETURN
|
|
165
|
+
IF(
|
|
166
|
+
ISBLANK(_Raw) || TRIM(_Raw) = "" || _Raw = "N/A" || _Raw = "NULL",
|
|
167
|
+
"(Unknown)",
|
|
168
|
+
TRIM(_Raw)
|
|
169
|
+
)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Context Validation
|
|
175
|
+
|
|
176
|
+
### HASONEVALUE - Single Selection Check
|
|
177
|
+
|
|
178
|
+
```dax
|
|
179
|
+
-- Only calculate if single value selected
|
|
180
|
+
Unit_Price_Display =
|
|
181
|
+
IF(
|
|
182
|
+
HASONEVALUE(Products[ProductName]),
|
|
183
|
+
SELECTEDVALUE(Products[UnitPrice]),
|
|
184
|
+
BLANK() -- Don't show if multiple products
|
|
185
|
+
)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### HASONEFILTER - Filter Check
|
|
189
|
+
|
|
190
|
+
```dax
|
|
191
|
+
-- Check if filter is applied
|
|
192
|
+
Status_Message =
|
|
193
|
+
IF(
|
|
194
|
+
HASONEFILTER(Products[Category]),
|
|
195
|
+
"Showing: " & SELECTEDVALUE(Products[Category]),
|
|
196
|
+
"All Categories"
|
|
197
|
+
)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### ISFILTERED / ISCROSSFILTERED
|
|
201
|
+
|
|
202
|
+
```dax
|
|
203
|
+
-- Validate filter state
|
|
204
|
+
Contextual_Result =
|
|
205
|
+
SWITCH(
|
|
206
|
+
TRUE(),
|
|
207
|
+
NOT ISFILTERED(Products[Category]), [Total_All],
|
|
208
|
+
ISFILTERED(Products[Category]) && NOT ISCROSSFILTERED(Regions), [Total_Category],
|
|
209
|
+
[Total_Filtered]
|
|
210
|
+
)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### SELECTEDVALUE with Fallback
|
|
214
|
+
|
|
215
|
+
```dax
|
|
216
|
+
-- Safe single value selection
|
|
217
|
+
Selected_Product =
|
|
218
|
+
SELECTEDVALUE(
|
|
219
|
+
Products[ProductName],
|
|
220
|
+
"(Multiple Products Selected)" -- Default when not single value
|
|
221
|
+
)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Date Validation
|
|
227
|
+
|
|
228
|
+
### Check for Valid Dates
|
|
229
|
+
|
|
230
|
+
```dax
|
|
231
|
+
-- Validate date exists
|
|
232
|
+
Days_Since_Order =
|
|
233
|
+
VAR _OrderDate = MAX(Sales[OrderDate])
|
|
234
|
+
VAR _Today = TODAY()
|
|
235
|
+
RETURN
|
|
236
|
+
IF(
|
|
237
|
+
ISBLANK(_OrderDate) || _OrderDate > _Today,
|
|
238
|
+
BLANK(),
|
|
239
|
+
DATEDIFF(_OrderDate, _Today, DAY)
|
|
240
|
+
)
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Handle Date Range Edge Cases
|
|
244
|
+
|
|
245
|
+
```dax
|
|
246
|
+
-- Safe date period calculation
|
|
247
|
+
Period_Sales =
|
|
248
|
+
VAR _StartDate = MIN('Date'[Date])
|
|
249
|
+
VAR _EndDate = MAX('Date'[Date])
|
|
250
|
+
RETURN
|
|
251
|
+
IF(
|
|
252
|
+
ISBLANK(_StartDate) || ISBLANK(_EndDate) || _StartDate > _EndDate,
|
|
253
|
+
BLANK(),
|
|
254
|
+
CALCULATE(
|
|
255
|
+
SUM(Sales[Amount]),
|
|
256
|
+
'Date'[Date] >= _StartDate && 'Date'[Date] <= _EndDate
|
|
257
|
+
)
|
|
258
|
+
)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Data Quality Measures
|
|
264
|
+
|
|
265
|
+
### Completeness Check
|
|
266
|
+
|
|
267
|
+
```dax
|
|
268
|
+
Data_Completeness =
|
|
269
|
+
VAR _TotalRows = COUNTROWS(Sales)
|
|
270
|
+
VAR _CompleteRows =
|
|
271
|
+
COUNTROWS(
|
|
272
|
+
FILTER(
|
|
273
|
+
Sales,
|
|
274
|
+
NOT ISBLANK(Sales[Amount])
|
|
275
|
+
&& NOT ISBLANK(Sales[CustomerID])
|
|
276
|
+
&& NOT ISBLANK(Sales[ProductID])
|
|
277
|
+
&& NOT ISBLANK(Sales[Date])
|
|
278
|
+
)
|
|
279
|
+
)
|
|
280
|
+
RETURN
|
|
281
|
+
DIVIDE(_CompleteRows, _TotalRows)
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Duplicate Detection
|
|
285
|
+
|
|
286
|
+
```dax
|
|
287
|
+
Duplicate_Orders =
|
|
288
|
+
VAR _UniqueOrders = DISTINCTCOUNT(Sales[OrderID])
|
|
289
|
+
VAR _TotalRows = COUNTROWS(Sales)
|
|
290
|
+
RETURN
|
|
291
|
+
IF(
|
|
292
|
+
_UniqueOrders < _TotalRows,
|
|
293
|
+
_TotalRows - _UniqueOrders,
|
|
294
|
+
0
|
|
295
|
+
)
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Outlier Detection
|
|
299
|
+
|
|
300
|
+
```dax
|
|
301
|
+
Outlier_Count =
|
|
302
|
+
VAR _Avg = AVERAGE(Sales[Amount])
|
|
303
|
+
VAR _StdDev =
|
|
304
|
+
SQRT(
|
|
305
|
+
AVERAGEX(Sales, POWER(Sales[Amount] - _Avg, 2))
|
|
306
|
+
)
|
|
307
|
+
VAR _LowerBound = _Avg - (3 * _StdDev)
|
|
308
|
+
VAR _UpperBound = _Avg + (3 * _StdDev)
|
|
309
|
+
RETURN
|
|
310
|
+
COUNTROWS(
|
|
311
|
+
FILTER(
|
|
312
|
+
Sales,
|
|
313
|
+
Sales[Amount] < _LowerBound || Sales[Amount] > _UpperBound
|
|
314
|
+
)
|
|
315
|
+
)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Conditional Logic Patterns
|
|
321
|
+
|
|
322
|
+
### SWITCH with Catch-All
|
|
323
|
+
|
|
324
|
+
```dax
|
|
325
|
+
-- Always include default case
|
|
326
|
+
Region_Group =
|
|
327
|
+
SWITCH(
|
|
328
|
+
SELECTEDVALUE(Geography[Country]),
|
|
329
|
+
"USA", "North America",
|
|
330
|
+
"Canada", "North America",
|
|
331
|
+
"Mexico", "North America",
|
|
332
|
+
"UK", "Europe",
|
|
333
|
+
"Germany", "Europe",
|
|
334
|
+
"France", "Europe",
|
|
335
|
+
"Other" -- Catch-all for unknown values
|
|
336
|
+
)
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### SWITCH TRUE Pattern
|
|
340
|
+
|
|
341
|
+
```dax
|
|
342
|
+
-- Complex conditional with error handling
|
|
343
|
+
Performance_Level =
|
|
344
|
+
VAR _Value = [Sales_vs_Target]
|
|
345
|
+
RETURN
|
|
346
|
+
SWITCH(
|
|
347
|
+
TRUE(),
|
|
348
|
+
ISBLANK(_Value), "No Data",
|
|
349
|
+
ISERROR(_Value), "Error",
|
|
350
|
+
_Value >= 1.2, "Exceeds",
|
|
351
|
+
_Value >= 1.0, "Meets",
|
|
352
|
+
_Value >= 0.8, "Below",
|
|
353
|
+
_Value < 0.8, "Critical",
|
|
354
|
+
"Unknown" -- Should never reach here
|
|
355
|
+
)
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Nested IF vs SWITCH
|
|
359
|
+
|
|
360
|
+
```dax
|
|
361
|
+
-- Prefer SWITCH for multiple conditions
|
|
362
|
+
-- Less error-prone than nested IFs
|
|
363
|
+
Status_SWITCH =
|
|
364
|
+
SWITCH(
|
|
365
|
+
TRUE(),
|
|
366
|
+
[DaysOverdue] > 90, "Critical",
|
|
367
|
+
[DaysOverdue] > 60, "Severe",
|
|
368
|
+
[DaysOverdue] > 30, "Warning",
|
|
369
|
+
[DaysOverdue] > 0, "Overdue",
|
|
370
|
+
[DaysOverdue] = 0, "Due Today",
|
|
371
|
+
"On Track"
|
|
372
|
+
)
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Debug Measures
|
|
378
|
+
|
|
379
|
+
### Show Calculation Context
|
|
380
|
+
|
|
381
|
+
```dax
|
|
382
|
+
-- Debug: Show current filter context
|
|
383
|
+
Debug_Context =
|
|
384
|
+
"Products: " & COUNTROWS(Products) &
|
|
385
|
+
" | Dates: " & COUNTROWS('Date') &
|
|
386
|
+
" | Filtered: " & IF(ISFILTERED(Products), "Yes", "No")
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Trace Measure Calculation
|
|
390
|
+
|
|
391
|
+
```dax
|
|
392
|
+
-- Debug: Step-by-step calculation
|
|
393
|
+
Debug_Calculation =
|
|
394
|
+
VAR _Step1 = SUM(Sales[Quantity])
|
|
395
|
+
VAR _Step2 = AVERAGE(Sales[UnitPrice])
|
|
396
|
+
VAR _Step3 = _Step1 * _Step2
|
|
397
|
+
VAR _Step4 = DIVIDE(_Step3, 100)
|
|
398
|
+
RETURN
|
|
399
|
+
"Qty:" & _Step1 &
|
|
400
|
+
" | AvgPrice:" & _Step2 &
|
|
401
|
+
" | Product:" & _Step3 &
|
|
402
|
+
" | Result:" & _Step4
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Validate Intermediate Results
|
|
406
|
+
|
|
407
|
+
```dax
|
|
408
|
+
-- Production measure with validation
|
|
409
|
+
Revenue_Validated =
|
|
410
|
+
VAR _Quantity = SUM(Sales[Quantity])
|
|
411
|
+
VAR _Price = AVERAGE(Sales[UnitPrice])
|
|
412
|
+
VAR _Result = _Quantity * _Price
|
|
413
|
+
RETURN
|
|
414
|
+
IF(
|
|
415
|
+
_Quantity < 0 || _Price < 0,
|
|
416
|
+
ERROR("Negative values detected in Revenue calculation"),
|
|
417
|
+
_Result
|
|
418
|
+
)
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## Best Practices
|
|
424
|
+
|
|
425
|
+
### 1. Always Use DIVIDE
|
|
426
|
+
|
|
427
|
+
```dax
|
|
428
|
+
-- Never do this
|
|
429
|
+
Bad_Ratio = [Sales] / [Customers]
|
|
430
|
+
|
|
431
|
+
-- Always do this
|
|
432
|
+
Good_Ratio = DIVIDE([Sales], [Customers])
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### 2. Validate Before Calculate
|
|
436
|
+
|
|
437
|
+
```dax
|
|
438
|
+
-- Check inputs before complex calculation
|
|
439
|
+
Complex_Result =
|
|
440
|
+
IF(
|
|
441
|
+
ISBLANK([Input1]) || ISBLANK([Input2]) || [Input2] = 0,
|
|
442
|
+
BLANK(),
|
|
443
|
+
[ComplexCalculation]
|
|
444
|
+
)
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### 3. Use Variables for Intermediate Checks
|
|
448
|
+
|
|
449
|
+
```dax
|
|
450
|
+
-- Capture values once, validate, then calculate
|
|
451
|
+
Safe_Growth =
|
|
452
|
+
VAR _Current = SUM(Sales[Amount])
|
|
453
|
+
VAR _Prior = [Sales_PY]
|
|
454
|
+
RETURN
|
|
455
|
+
IF(
|
|
456
|
+
ISBLANK(_Current) || ISBLANK(_Prior) || _Prior = 0,
|
|
457
|
+
BLANK(),
|
|
458
|
+
DIVIDE(_Current - _Prior, _Prior)
|
|
459
|
+
)
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### 4. Handle Edge Cases Explicitly
|
|
463
|
+
|
|
464
|
+
```dax
|
|
465
|
+
-- Consider all scenarios
|
|
466
|
+
Date_Status =
|
|
467
|
+
VAR _Date = SELECTEDVALUE(Sales[DueDate])
|
|
468
|
+
VAR _Today = TODAY()
|
|
469
|
+
RETURN
|
|
470
|
+
SWITCH(
|
|
471
|
+
TRUE(),
|
|
472
|
+
ISBLANK(_Date), "No Due Date",
|
|
473
|
+
_Date < DATE(1900, 1, 1), "Invalid Date",
|
|
474
|
+
_Date > DATE(2100, 12, 31), "Invalid Date",
|
|
475
|
+
_Date < _Today, "Past Due",
|
|
476
|
+
_Date = _Today, "Due Today",
|
|
477
|
+
_Date <= _Today + 7, "Due This Week",
|
|
478
|
+
"Upcoming"
|
|
479
|
+
)
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## Error Prevention Checklist
|
|
485
|
+
|
|
486
|
+
| Issue | Prevention |
|
|
487
|
+
|-------|------------|
|
|
488
|
+
| Division by zero | Use DIVIDE() |
|
|
489
|
+
| Blank values | ISBLANK() checks, COALESCE() |
|
|
490
|
+
| Missing data | SELECTEDVALUE with alternate |
|
|
491
|
+
| Invalid dates | Date range validation |
|
|
492
|
+
| Context errors | HASONEVALUE, ISFILTERED |
|
|
493
|
+
| Type mismatches | Explicit conversions |
|
|
494
|
+
| Calculation errors | IFERROR wrapper |
|
|
495
|
+
| Edge cases | SWITCH TRUE with default |
|