@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,546 @@
|
|
|
1
|
+
# Virtual Tables in DAX
|
|
2
|
+
|
|
3
|
+
Patterns for creating and using virtual tables with SUMMARIZE, ADDCOLUMNS, SELECTCOLUMNS, GENERATE, and table functions.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Why Use Virtual Tables? (Business Perspective)
|
|
8
|
+
|
|
9
|
+
**The Problem:** You need to perform calculations that require intermediate steps, like ranking products within each category or calculating running totals.
|
|
10
|
+
|
|
11
|
+
**The Solution:** Virtual tables let you create temporary "helper tables" during calculation that exist only while the formula runs.
|
|
12
|
+
|
|
13
|
+
### Real-World Business Examples
|
|
14
|
+
|
|
15
|
+
| Business Need | Virtual Table Approach |
|
|
16
|
+
|--------------|------------------------|
|
|
17
|
+
| Top 5 products per region | TOPN + SUMMARIZE creates ranked subsets |
|
|
18
|
+
| ABC analysis (Pareto) | ADDCOLUMNS + cumulative % classification |
|
|
19
|
+
| Sales vs. comparable products | GENERATE creates product pairs for comparison |
|
|
20
|
+
| Year-over-year by segment | SUMMARIZE groups data for time comparison |
|
|
21
|
+
|
|
22
|
+
### When Should You Use Virtual Tables?
|
|
23
|
+
|
|
24
|
+
**Use virtual tables when:**
|
|
25
|
+
- You need intermediate calculations (e.g., rank, then filter, then sum)
|
|
26
|
+
- You're building complex metrics that require grouped data
|
|
27
|
+
- You need to create temporary lookup structures
|
|
28
|
+
- Standard measures can't express your business logic
|
|
29
|
+
|
|
30
|
+
**Be careful when:**
|
|
31
|
+
- Tables are very large (millions of rows) - consider query performance
|
|
32
|
+
- You can achieve the same result with simpler CALCULATE patterns
|
|
33
|
+
- You're nesting too many virtual tables (hard to debug)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Common Mistakes to Avoid
|
|
38
|
+
|
|
39
|
+
| Mistake | Problem | Solution |
|
|
40
|
+
|---------|---------|----------|
|
|
41
|
+
| SUMMARIZE with measures | Unreliable context behavior | Use ADDCOLUMNS + SUMMARIZE pattern |
|
|
42
|
+
| Large virtual tables in visuals | Slow report rendering | Materialize to a physical table or simplify |
|
|
43
|
+
| Not using VAR | Recalculates table multiple times | Store in VAR for reuse |
|
|
44
|
+
| Nested FILTER inside SUMX | Quadratic performance | Pre-filter with CALCULATETABLE |
|
|
45
|
+
| Using ALL(Table) vs VALUES | Wrong granularity | VALUES respects filters, ALL ignores them |
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## SUMMARIZE Patterns
|
|
50
|
+
|
|
51
|
+
### Basic Grouping
|
|
52
|
+
|
|
53
|
+
```dax
|
|
54
|
+
-- Summarize sales by category
|
|
55
|
+
CategorySummary =
|
|
56
|
+
SUMMARIZE(
|
|
57
|
+
Sales,
|
|
58
|
+
Products[Category],
|
|
59
|
+
"Total Sales", SUM(Sales[Amount]),
|
|
60
|
+
"Order Count", COUNTROWS(Sales)
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Multi-Level Grouping
|
|
65
|
+
|
|
66
|
+
```dax
|
|
67
|
+
-- Summarize by category and year
|
|
68
|
+
Category_Year_Summary =
|
|
69
|
+
SUMMARIZE(
|
|
70
|
+
Sales,
|
|
71
|
+
Products[Category],
|
|
72
|
+
'Date'[Year],
|
|
73
|
+
"Total Sales", SUM(Sales[Amount]),
|
|
74
|
+
"Avg Order", AVERAGE(Sales[Amount])
|
|
75
|
+
)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### SUMMARIZE with ROLLUP
|
|
79
|
+
|
|
80
|
+
```dax
|
|
81
|
+
-- Include subtotals
|
|
82
|
+
SummaryWithSubtotals =
|
|
83
|
+
SUMMARIZE(
|
|
84
|
+
Sales,
|
|
85
|
+
ROLLUP(Products[Category], Products[SubCategory]),
|
|
86
|
+
"Sales", SUM(Sales[Amount])
|
|
87
|
+
)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Safe SUMMARIZE Pattern
|
|
91
|
+
|
|
92
|
+
```dax
|
|
93
|
+
-- Best practice: Use ADDCOLUMNS with SUMMARIZE
|
|
94
|
+
CategorySales_Safe =
|
|
95
|
+
ADDCOLUMNS(
|
|
96
|
+
SUMMARIZE(
|
|
97
|
+
Sales,
|
|
98
|
+
Products[Category]
|
|
99
|
+
),
|
|
100
|
+
"@Sales", CALCULATE(SUM(Sales[Amount])),
|
|
101
|
+
"@Orders", CALCULATE(COUNTROWS(Sales))
|
|
102
|
+
)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## ADDCOLUMNS Patterns
|
|
108
|
+
|
|
109
|
+
### Extend Table with Calculated Columns
|
|
110
|
+
|
|
111
|
+
```dax
|
|
112
|
+
-- Add calculated columns to product table
|
|
113
|
+
ProductMetrics =
|
|
114
|
+
ADDCOLUMNS(
|
|
115
|
+
VALUES(Products[ProductName]),
|
|
116
|
+
"@TotalSales", CALCULATE(SUM(Sales[Amount])),
|
|
117
|
+
"@TotalUnits", CALCULATE(SUM(Sales[Quantity])),
|
|
118
|
+
"@AvgPrice", CALCULATE(AVERAGE(Sales[UnitPrice]))
|
|
119
|
+
)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Multi-Level Calculations
|
|
123
|
+
|
|
124
|
+
```dax
|
|
125
|
+
-- Add columns in stages for complex calculations
|
|
126
|
+
CustomerAnalysis =
|
|
127
|
+
VAR _Base =
|
|
128
|
+
ADDCOLUMNS(
|
|
129
|
+
VALUES(Customers[CustomerID]),
|
|
130
|
+
"@Sales", CALCULATE(SUM(Sales[Amount])),
|
|
131
|
+
"@Orders", CALCULATE(COUNTROWS(Sales))
|
|
132
|
+
)
|
|
133
|
+
RETURN
|
|
134
|
+
ADDCOLUMNS(
|
|
135
|
+
_Base,
|
|
136
|
+
"@AvgOrderValue", DIVIDE([@Sales], [@Orders]),
|
|
137
|
+
"@Tier", IF([@Sales] > 10000, "Premium", IF([@Sales] > 5000, "Standard", "Basic"))
|
|
138
|
+
)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### ADDCOLUMNS for KPI Dashboard
|
|
142
|
+
|
|
143
|
+
```dax
|
|
144
|
+
-- Create KPI virtual table
|
|
145
|
+
KPI_Table =
|
|
146
|
+
ADDCOLUMNS(
|
|
147
|
+
SUMMARIZE(
|
|
148
|
+
Sales,
|
|
149
|
+
'Date'[Year],
|
|
150
|
+
'Date'[Month]
|
|
151
|
+
),
|
|
152
|
+
"@Revenue", CALCULATE(SUM(Sales[Amount])),
|
|
153
|
+
"@Cost", CALCULATE(SUM(Sales[Cost])),
|
|
154
|
+
"@Profit", CALCULATE(SUM(Sales[Amount]) - SUM(Sales[Cost])),
|
|
155
|
+
"@Margin", DIVIDE(
|
|
156
|
+
CALCULATE(SUM(Sales[Amount]) - SUM(Sales[Cost])),
|
|
157
|
+
CALCULATE(SUM(Sales[Amount]))
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## SELECTCOLUMNS Patterns
|
|
165
|
+
|
|
166
|
+
### Select Specific Columns
|
|
167
|
+
|
|
168
|
+
```dax
|
|
169
|
+
-- Create slim table with renamed columns
|
|
170
|
+
CustomerList =
|
|
171
|
+
SELECTCOLUMNS(
|
|
172
|
+
Customers,
|
|
173
|
+
"ID", Customers[CustomerID],
|
|
174
|
+
"Name", Customers[CustomerName],
|
|
175
|
+
"Email", Customers[Email]
|
|
176
|
+
)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Transform and Rename
|
|
180
|
+
|
|
181
|
+
```dax
|
|
182
|
+
-- Transform column values
|
|
183
|
+
ProductPricing =
|
|
184
|
+
SELECTCOLUMNS(
|
|
185
|
+
Products,
|
|
186
|
+
"Product", Products[ProductName],
|
|
187
|
+
"List Price", Products[Price],
|
|
188
|
+
"Discounted", Products[Price] * 0.9,
|
|
189
|
+
"Premium", Products[Price] * 1.2
|
|
190
|
+
)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Combine with Filter
|
|
194
|
+
|
|
195
|
+
```dax
|
|
196
|
+
-- Select from filtered table
|
|
197
|
+
ActiveCustomers =
|
|
198
|
+
SELECTCOLUMNS(
|
|
199
|
+
FILTER(
|
|
200
|
+
Customers,
|
|
201
|
+
Customers[Status] = "Active"
|
|
202
|
+
),
|
|
203
|
+
"Customer", Customers[CustomerName],
|
|
204
|
+
"Since", Customers[JoinDate]
|
|
205
|
+
)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## GENERATE / GENERATEALL Patterns
|
|
211
|
+
|
|
212
|
+
### Cross Join with Calculation
|
|
213
|
+
|
|
214
|
+
```dax
|
|
215
|
+
-- Generate customer-product matrix
|
|
216
|
+
Customer_Product_Matrix =
|
|
217
|
+
GENERATE(
|
|
218
|
+
VALUES(Customers[CustomerName]),
|
|
219
|
+
ADDCOLUMNS(
|
|
220
|
+
VALUES(Products[ProductName]),
|
|
221
|
+
"@Sales", CALCULATE(SUM(Sales[Amount]))
|
|
222
|
+
)
|
|
223
|
+
)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Date-Based Generation
|
|
227
|
+
|
|
228
|
+
```dax
|
|
229
|
+
-- Generate date ranges with metrics
|
|
230
|
+
Monthly_Metrics =
|
|
231
|
+
GENERATE(
|
|
232
|
+
VALUES('Date'[YearMonth]),
|
|
233
|
+
ROW(
|
|
234
|
+
"@Sales", CALCULATE(SUM(Sales[Amount])),
|
|
235
|
+
"@Units", CALCULATE(SUM(Sales[Quantity])),
|
|
236
|
+
"@Customers", CALCULATE(DISTINCTCOUNT(Sales[CustomerID]))
|
|
237
|
+
)
|
|
238
|
+
)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### GENERATEALL for Complete Matrix
|
|
242
|
+
|
|
243
|
+
```dax
|
|
244
|
+
-- Include empty combinations
|
|
245
|
+
Full_Matrix =
|
|
246
|
+
GENERATEALL(
|
|
247
|
+
VALUES(Products[Category]),
|
|
248
|
+
ADDCOLUMNS(
|
|
249
|
+
VALUES('Date'[Year]),
|
|
250
|
+
"@Sales", CALCULATE(SUM(Sales[Amount]))
|
|
251
|
+
)
|
|
252
|
+
)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## UNION / INTERSECT / EXCEPT Patterns
|
|
258
|
+
|
|
259
|
+
### UNION - Combine Tables
|
|
260
|
+
|
|
261
|
+
```dax
|
|
262
|
+
-- Combine actual and budget
|
|
263
|
+
Actual_And_Budget =
|
|
264
|
+
UNION(
|
|
265
|
+
SELECTCOLUMNS(
|
|
266
|
+
ActualSales,
|
|
267
|
+
"Type", "Actual",
|
|
268
|
+
"Date", ActualSales[Date],
|
|
269
|
+
"Amount", ActualSales[Amount]
|
|
270
|
+
),
|
|
271
|
+
SELECTCOLUMNS(
|
|
272
|
+
BudgetSales,
|
|
273
|
+
"Type", "Budget",
|
|
274
|
+
"Date", BudgetSales[Date],
|
|
275
|
+
"Amount", BudgetSales[Amount]
|
|
276
|
+
)
|
|
277
|
+
)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### INTERSECT - Common Values
|
|
281
|
+
|
|
282
|
+
```dax
|
|
283
|
+
-- Customers who bought both categories
|
|
284
|
+
Crossover_Customers =
|
|
285
|
+
VAR _ElectronicsBuyers =
|
|
286
|
+
CALCULATETABLE(
|
|
287
|
+
VALUES(Sales[CustomerID]),
|
|
288
|
+
Products[Category] = "Electronics"
|
|
289
|
+
)
|
|
290
|
+
VAR _ClothingBuyers =
|
|
291
|
+
CALCULATETABLE(
|
|
292
|
+
VALUES(Sales[CustomerID]),
|
|
293
|
+
Products[Category] = "Clothing"
|
|
294
|
+
)
|
|
295
|
+
RETURN
|
|
296
|
+
COUNTROWS(INTERSECT(_ElectronicsBuyers, _ClothingBuyers))
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### EXCEPT - Difference
|
|
300
|
+
|
|
301
|
+
```dax
|
|
302
|
+
-- Products never sold
|
|
303
|
+
Unsold_Products =
|
|
304
|
+
EXCEPT(
|
|
305
|
+
ALL(Products[ProductID]),
|
|
306
|
+
VALUES(Sales[ProductID])
|
|
307
|
+
)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## DATATABLE / ROW Patterns
|
|
313
|
+
|
|
314
|
+
### Static Reference Table
|
|
315
|
+
|
|
316
|
+
```dax
|
|
317
|
+
-- Create status reference
|
|
318
|
+
StatusColors =
|
|
319
|
+
DATATABLE(
|
|
320
|
+
"Status", STRING,
|
|
321
|
+
"Color", STRING,
|
|
322
|
+
"Priority", INTEGER,
|
|
323
|
+
{
|
|
324
|
+
{"Critical", "#FF0000", 1},
|
|
325
|
+
{"Warning", "#FFA500", 2},
|
|
326
|
+
{"Normal", "#00FF00", 3},
|
|
327
|
+
{"Low", "#808080", 4}
|
|
328
|
+
}
|
|
329
|
+
)
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Single Row Table
|
|
333
|
+
|
|
334
|
+
```dax
|
|
335
|
+
-- Create single row for calculation
|
|
336
|
+
Thresholds =
|
|
337
|
+
ROW(
|
|
338
|
+
"LowThreshold", 0.1,
|
|
339
|
+
"MediumThreshold", 0.5,
|
|
340
|
+
"HighThreshold", 0.9
|
|
341
|
+
)
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Combine with LOOKUPVALUE
|
|
345
|
+
|
|
346
|
+
```dax
|
|
347
|
+
-- Lookup from static table
|
|
348
|
+
StatusColor =
|
|
349
|
+
VAR _Status = [OrderStatus]
|
|
350
|
+
RETURN
|
|
351
|
+
LOOKUPVALUE(
|
|
352
|
+
StatusColors[Color],
|
|
353
|
+
StatusColors[Status], _Status
|
|
354
|
+
)
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## NATURALINNERJOIN / NATURALLEFTOUTERJOIN
|
|
360
|
+
|
|
361
|
+
### Inner Join Tables
|
|
362
|
+
|
|
363
|
+
```dax
|
|
364
|
+
-- Join sales with products
|
|
365
|
+
SalesWithProduct =
|
|
366
|
+
NATURALINNERJOIN(
|
|
367
|
+
SELECTCOLUMNS(Sales, "ProductID", Sales[ProductID], "Amount", Sales[Amount]),
|
|
368
|
+
SELECTCOLUMNS(Products, "ProductID", Products[ProductID], "Name", Products[ProductName])
|
|
369
|
+
)
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Left Outer Join
|
|
373
|
+
|
|
374
|
+
```dax
|
|
375
|
+
-- All products with optional sales data
|
|
376
|
+
ProductsWithSales =
|
|
377
|
+
NATURALLEFTOUTERJOIN(
|
|
378
|
+
SELECTCOLUMNS(Products, "ProductID", Products[ProductID], "Name", Products[ProductName]),
|
|
379
|
+
ADDCOLUMNS(
|
|
380
|
+
SUMMARIZE(Sales, Sales[ProductID]),
|
|
381
|
+
"@Sales", CALCULATE(SUM(Sales[Amount]))
|
|
382
|
+
)
|
|
383
|
+
)
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## TREATAS Pattern
|
|
389
|
+
|
|
390
|
+
### Virtual Relationship
|
|
391
|
+
|
|
392
|
+
```dax
|
|
393
|
+
-- Apply filter from disconnected table
|
|
394
|
+
Sales_Budget_Products =
|
|
395
|
+
CALCULATE(
|
|
396
|
+
SUM(Sales[Amount]),
|
|
397
|
+
TREATAS(
|
|
398
|
+
VALUES(Budget[ProductID]),
|
|
399
|
+
Products[ProductID]
|
|
400
|
+
)
|
|
401
|
+
)
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Cross-Table Analysis
|
|
405
|
+
|
|
406
|
+
```dax
|
|
407
|
+
-- Compare two disconnected tables
|
|
408
|
+
Budget_vs_Actual =
|
|
409
|
+
VAR _BudgetProducts = VALUES(Budget[ProductID])
|
|
410
|
+
VAR _ActualSales =
|
|
411
|
+
CALCULATE(
|
|
412
|
+
SUM(Sales[Amount]),
|
|
413
|
+
TREATAS(_BudgetProducts, Products[ProductID])
|
|
414
|
+
)
|
|
415
|
+
VAR _BudgetAmount = SUM(Budget[Amount])
|
|
416
|
+
RETURN
|
|
417
|
+
DIVIDE(_ActualSales - _BudgetAmount, _BudgetAmount)
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## Performance Patterns
|
|
423
|
+
|
|
424
|
+
### Materialized vs Non-Materialized
|
|
425
|
+
|
|
426
|
+
```dax
|
|
427
|
+
-- Good: Materialize once, use multiple times
|
|
428
|
+
Calculation_Optimized =
|
|
429
|
+
VAR _CustomerTable =
|
|
430
|
+
ADDCOLUMNS(
|
|
431
|
+
VALUES(Customers[CustomerID]),
|
|
432
|
+
"@Sales", CALCULATE(SUM(Sales[Amount])),
|
|
433
|
+
"@Orders", CALCULATE(COUNTROWS(Sales))
|
|
434
|
+
)
|
|
435
|
+
VAR _AvgSales = AVERAGEX(_CustomerTable, [@Sales])
|
|
436
|
+
VAR _AvgOrders = AVERAGEX(_CustomerTable, [@Orders])
|
|
437
|
+
RETURN
|
|
438
|
+
DIVIDE(_AvgSales, _AvgOrders)
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Avoid Repeated Calculation
|
|
442
|
+
|
|
443
|
+
```dax
|
|
444
|
+
-- Bad: Calculates multiple times
|
|
445
|
+
Result_Bad =
|
|
446
|
+
SUMX(
|
|
447
|
+
ADDCOLUMNS(
|
|
448
|
+
Products,
|
|
449
|
+
"@Sales", CALCULATE(SUM(Sales[Amount]))
|
|
450
|
+
),
|
|
451
|
+
[@Sales] * 0.1
|
|
452
|
+
) +
|
|
453
|
+
SUMX(
|
|
454
|
+
ADDCOLUMNS(
|
|
455
|
+
Products,
|
|
456
|
+
"@Sales", CALCULATE(SUM(Sales[Amount])) -- Repeated!
|
|
457
|
+
),
|
|
458
|
+
[@Sales] * 0.05
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
-- Good: Calculate once
|
|
462
|
+
Result_Good =
|
|
463
|
+
VAR _ProductSales =
|
|
464
|
+
ADDCOLUMNS(
|
|
465
|
+
ALL(Products),
|
|
466
|
+
"@Sales", CALCULATE(SUM(Sales[Amount]))
|
|
467
|
+
)
|
|
468
|
+
RETURN
|
|
469
|
+
SUMX(_ProductSales, [@Sales] * 0.1) +
|
|
470
|
+
SUMX(_ProductSales, [@Sales] * 0.05)
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Common Virtual Table Measure Pattern
|
|
476
|
+
|
|
477
|
+
### Measure That Returns Table Info
|
|
478
|
+
|
|
479
|
+
```dax
|
|
480
|
+
-- Count of profitable products
|
|
481
|
+
Profitable_Product_Count =
|
|
482
|
+
VAR _ProductProfits =
|
|
483
|
+
ADDCOLUMNS(
|
|
484
|
+
ALL(Products[ProductID]),
|
|
485
|
+
"@Profit", CALCULATE(SUM(Sales[Amount]) - SUM(Sales[Cost]))
|
|
486
|
+
)
|
|
487
|
+
VAR _ProfitableProducts =
|
|
488
|
+
FILTER(_ProductProfits, [@Profit] > 0)
|
|
489
|
+
RETURN
|
|
490
|
+
COUNTROWS(_ProfitableProducts)
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### Dynamic ABC Classification
|
|
494
|
+
|
|
495
|
+
```dax
|
|
496
|
+
-- ABC analysis using virtual table
|
|
497
|
+
ABC_Class =
|
|
498
|
+
VAR _AllProducts =
|
|
499
|
+
ADDCOLUMNS(
|
|
500
|
+
ALL(Products[ProductName]),
|
|
501
|
+
"@Sales", CALCULATE(SUM(Sales[Amount]))
|
|
502
|
+
)
|
|
503
|
+
VAR _TotalSales = SUMX(_AllProducts, [@Sales])
|
|
504
|
+
VAR _Sorted =
|
|
505
|
+
ADDCOLUMNS(
|
|
506
|
+
_AllProducts,
|
|
507
|
+
"@CumPct",
|
|
508
|
+
VAR _CurrentSales = [@Sales]
|
|
509
|
+
VAR _Higher = SUMX(FILTER(_AllProducts, [@Sales] >= _CurrentSales), [@Sales])
|
|
510
|
+
RETURN DIVIDE(_Higher, _TotalSales)
|
|
511
|
+
)
|
|
512
|
+
VAR _CurrentProduct = SELECTEDVALUE(Products[ProductName])
|
|
513
|
+
VAR _CumPct =
|
|
514
|
+
MAXX(FILTER(_Sorted, [ProductName] = _CurrentProduct), [@CumPct])
|
|
515
|
+
RETURN
|
|
516
|
+
SWITCH(
|
|
517
|
+
TRUE(),
|
|
518
|
+
_CumPct <= 0.7, "A",
|
|
519
|
+
_CumPct <= 0.9, "B",
|
|
520
|
+
"C"
|
|
521
|
+
)
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
---
|
|
525
|
+
|
|
526
|
+
## Usage Notes
|
|
527
|
+
|
|
528
|
+
1. **Virtual tables exist only during calculation** - They're not stored in the model
|
|
529
|
+
2. **Use ADDCOLUMNS + SUMMARIZE** - Safer than putting aggregations directly in SUMMARIZE
|
|
530
|
+
3. **Materialize to variables** - Store virtual tables in VAR for reuse
|
|
531
|
+
4. **SELECTCOLUMNS for slim tables** - Reduces memory when you only need specific columns
|
|
532
|
+
5. **GENERATE for cross-joins** - Creates row combinations with calculations
|
|
533
|
+
6. **TREATAS for virtual relationships** - Applies filters across disconnected tables
|
|
534
|
+
|
|
535
|
+
## Function Comparison
|
|
536
|
+
|
|
537
|
+
| Function | Purpose | Returns |
|
|
538
|
+
|----------|---------|---------|
|
|
539
|
+
| SUMMARIZE | Group and aggregate | Grouped table |
|
|
540
|
+
| ADDCOLUMNS | Add calculated columns | Extended table |
|
|
541
|
+
| SELECTCOLUMNS | Pick/rename columns | Slimmed table |
|
|
542
|
+
| GENERATE | Cross-join with calc | Combined table |
|
|
543
|
+
| FILTER | Row-wise filter | Filtered table |
|
|
544
|
+
| DISTINCT/VALUES | Unique values | Distinct table |
|
|
545
|
+
| UNION | Combine tables | Merged table |
|
|
546
|
+
| TREATAS | Apply lineage | Filtered table |
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Excel Formula Snippets
|
|
2
|
+
|
|
3
|
+
Collection of reusable Excel formulas for modern Excel (365/2021+).
|
|
4
|
+
|
|
5
|
+
## Available Snippets
|
|
6
|
+
|
|
7
|
+
| File | Description |
|
|
8
|
+
|------|-------------|
|
|
9
|
+
| [lookups.md](./lookups.md) | XLOOKUP, INDEX/MATCH, VLOOKUP patterns |
|
|
10
|
+
| [dynamic-arrays.md](./dynamic-arrays.md) | FILTER, SORT, UNIQUE, LET, SEQUENCE |
|
|
11
|
+
| [aggregations.md](./aggregations.md) | SUMIFS, COUNTIFS, AVERAGEIFS, MAXIFS |
|
|
12
|
+
| [dates-and-times.md](./dates-and-times.md) | Date calculations, workdays, age |
|
|
13
|
+
| [text-functions.md](./text-functions.md) | Text manipulation, extraction, parsing |
|
|
14
|
+
|
|
15
|
+
## Quick Reference
|
|
16
|
+
|
|
17
|
+
### Lookups
|
|
18
|
+
```excel
|
|
19
|
+
=XLOOKUP(A2, tbl_Products[ID], tbl_Products[Name], "Not Found")
|
|
20
|
+
=INDEX(Range, MATCH(A2, LookupRange, 0))
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Dynamic Arrays
|
|
24
|
+
```excel
|
|
25
|
+
=FILTER(tbl_Sales, tbl_Sales[Region]="North")
|
|
26
|
+
=SORT(UNIQUE(tbl_Sales[Customer]))
|
|
27
|
+
=TAKE(SORT(tbl_Sales, 3, -1), 10)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Aggregations
|
|
31
|
+
```excel
|
|
32
|
+
=SUMIFS(tbl_Sales[Amount], tbl_Sales[Region], "North", tbl_Sales[Year], 2024)
|
|
33
|
+
=AVERAGEIFS(tbl_Sales[Amount], tbl_Sales[Product], A2)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### LET Pattern
|
|
37
|
+
```excel
|
|
38
|
+
=LET(
|
|
39
|
+
data, FILTER(tbl_Sales, tbl_Sales[Active]=TRUE),
|
|
40
|
+
total, SUM(INDEX(data, , 3)),
|
|
41
|
+
total
|
|
42
|
+
)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Categories
|
|
46
|
+
|
|
47
|
+
- **Lookups**: XLOOKUP, INDEX/MATCH, VLOOKUP, approximate matches
|
|
48
|
+
- **Dynamic Arrays**: FILTER, SORT, UNIQUE, SEQUENCE, TAKE, DROP
|
|
49
|
+
- **Aggregations**: SUMIFS, COUNTIFS, conditional aggregation
|
|
50
|
+
- **Dates**: WORKDAY, NETWORKDAYS, EOMONTH, date math
|
|
51
|
+
- **Text**: TEXTJOIN, TEXTSPLIT, parsing and formatting
|
|
52
|
+
|
|
53
|
+
## Best Practices
|
|
54
|
+
|
|
55
|
+
1. Use structured references: `tbl_Sales[Amount]` not `C:C`
|
|
56
|
+
2. Prefer XLOOKUP over VLOOKUP for new spreadsheets
|
|
57
|
+
3. Use LET for complex formulas (readability + performance)
|
|
58
|
+
4. Prefer FILTER over helper columns for dynamic lists
|
|
59
|
+
5. Use table names with `tbl_` prefix for clarity
|
|
60
|
+
|
|
61
|
+
## Dynamic Array Tips
|
|
62
|
+
|
|
63
|
+
- Arrays spill automatically to adjacent cells
|
|
64
|
+
- Reference spill range with `#`: `=SUM(A1#)`
|
|
65
|
+
- Use `@` for single value: `=@SORT(A:A)`
|
|
66
|
+
- Combine functions: `=SORT(FILTER(UNIQUE(...)))`
|
|
67
|
+
|
|
68
|
+
## Compatibility Notes
|
|
69
|
+
|
|
70
|
+
| Feature | Excel 365 | Excel 2021 | Excel 2019 |
|
|
71
|
+
|---------|-----------|------------|------------|
|
|
72
|
+
| XLOOKUP | Yes | Yes | No |
|
|
73
|
+
| FILTER | Yes | Yes | No |
|
|
74
|
+
| LET | Yes | Yes | No |
|
|
75
|
+
| LAMBDA | Yes | No | No |
|
|
76
|
+
| TAKE/DROP | Yes | No | No |
|
|
77
|
+
|
|
78
|
+
## Contributing
|
|
79
|
+
|
|
80
|
+
Add new snippets as `.md` files with:
|
|
81
|
+
- Clear title and description
|
|
82
|
+
- Working formula examples
|
|
83
|
+
- Explanation of parameters
|
|
84
|
+
- Compatibility notes if needed
|