@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.
Files changed (193) hide show
  1. package/.claude-plugin/plugin.json +8 -0
  2. package/.mcp.json +25 -0
  3. package/AGENTS.md +244 -0
  4. package/CHANGELOG.md +265 -0
  5. package/LICENSE +21 -0
  6. package/README.md +211 -0
  7. package/bin/build-plugin.js +30 -0
  8. package/bin/cli.js +1064 -0
  9. package/bin/commands/add.js +533 -0
  10. package/bin/commands/add.test.js +77 -0
  11. package/bin/commands/build-desktop.js +166 -0
  12. package/bin/commands/changelog.js +443 -0
  13. package/bin/commands/diff.js +325 -0
  14. package/bin/commands/lint.js +419 -0
  15. package/bin/commands/lint.test.js +103 -0
  16. package/bin/commands/mcp-setup.js +246 -0
  17. package/bin/commands/pull.js +287 -0
  18. package/bin/commands/pull.test.js +36 -0
  19. package/bin/commands/push.js +231 -0
  20. package/bin/commands/push.test.js +14 -0
  21. package/bin/commands/search.js +344 -0
  22. package/bin/commands/search.test.js +115 -0
  23. package/bin/commands/setup.js +545 -0
  24. package/bin/commands/setup.test.js +46 -0
  25. package/bin/commands/sync-profile.js +405 -0
  26. package/bin/commands/sync-profile.test.js +14 -0
  27. package/bin/commands/sync-source.js +418 -0
  28. package/bin/commands/sync-source.test.js +14 -0
  29. package/bin/commands/watch.js +206 -0
  30. package/bin/lib/generators/claude-plugin.js +266 -0
  31. package/bin/lib/generators/claude-plugin.test.js +110 -0
  32. package/bin/lib/generators/index.js +116 -0
  33. package/bin/lib/generators/shared.js +282 -0
  34. package/bin/lib/licensing/index.js +35 -0
  35. package/bin/lib/licensing/storage.js +364 -0
  36. package/bin/lib/licensing/storage.test.js +55 -0
  37. package/bin/lib/licensing/validator.js +213 -0
  38. package/bin/lib/licensing/validator.test.js +137 -0
  39. package/bin/lib/microsoft-mcp.js +176 -0
  40. package/bin/lib/microsoft-mcp.test.js +106 -0
  41. package/bin/lib/skills.js +84 -0
  42. package/bin/mcp/powerbi-modeling-launcher.js +38 -0
  43. package/bin/postinstall.js +44 -0
  44. package/bin/utils/errors.js +159 -0
  45. package/bin/utils/git.js +298 -0
  46. package/bin/utils/logger.js +142 -0
  47. package/bin/utils/mcp-detect.js +274 -0
  48. package/bin/utils/mcp-detect.test.js +105 -0
  49. package/bin/utils/pbix.js +305 -0
  50. package/bin/utils/pbix.test.js +37 -0
  51. package/bin/utils/profiles.js +312 -0
  52. package/bin/utils/projects.js +168 -0
  53. package/bin/utils/readline.js +206 -0
  54. package/bin/utils/readline.test.js +47 -0
  55. package/bin/utils/tui.js +314 -0
  56. package/bin/utils/tui.test.js +127 -0
  57. package/commands/contributions.md +265 -0
  58. package/commands/data-model-design.md +468 -0
  59. package/commands/dax-doctor.md +248 -0
  60. package/commands/fabric-scripts.md +452 -0
  61. package/commands/migration-assistant.md +290 -0
  62. package/commands/model-documenter.md +242 -0
  63. package/commands/pbi-connect.md +239 -0
  64. package/commands/project-kickoff.md +905 -0
  65. package/commands/report-layout.md +296 -0
  66. package/commands/rls-design.md +533 -0
  67. package/commands/theme-tweaker.md +624 -0
  68. package/config.example.json +23 -0
  69. package/config.json +23 -0
  70. package/desktop-extension/manifest.json +37 -0
  71. package/desktop-extension/package.json +10 -0
  72. package/desktop-extension/server.js +95 -0
  73. package/docs/openrouter-free-models.md +92 -0
  74. package/library/examples/README.md +151 -0
  75. package/library/examples/finance-reporting/README.md +351 -0
  76. package/library/examples/finance-reporting/data-model.md +267 -0
  77. package/library/examples/finance-reporting/measures.dax +557 -0
  78. package/library/examples/hr-analytics/README.md +371 -0
  79. package/library/examples/hr-analytics/data-model.md +315 -0
  80. package/library/examples/hr-analytics/measures.dax +460 -0
  81. package/library/examples/marketing-analytics/README.md +37 -0
  82. package/library/examples/marketing-analytics/data-model.md +62 -0
  83. package/library/examples/marketing-analytics/measures.dax +110 -0
  84. package/library/examples/retail-analytics/README.md +439 -0
  85. package/library/examples/retail-analytics/data-model.md +288 -0
  86. package/library/examples/retail-analytics/measures.dax +481 -0
  87. package/library/examples/supply-chain/README.md +37 -0
  88. package/library/examples/supply-chain/data-model.md +69 -0
  89. package/library/examples/supply-chain/measures.dax +77 -0
  90. package/library/examples/udf-library/README.md +228 -0
  91. package/library/examples/udf-library/functions.dax +571 -0
  92. package/library/snippets/dax/README.md +292 -0
  93. package/library/snippets/dax/business-domains.md +576 -0
  94. package/library/snippets/dax/calculate-patterns.md +276 -0
  95. package/library/snippets/dax/calculation-groups.md +489 -0
  96. package/library/snippets/dax/error-handling.md +495 -0
  97. package/library/snippets/dax/iterators-and-aggregations.md +474 -0
  98. package/library/snippets/dax/kpis-and-metrics.md +293 -0
  99. package/library/snippets/dax/rankings-and-topn.md +235 -0
  100. package/library/snippets/dax/security-patterns.md +413 -0
  101. package/library/snippets/dax/text-and-formatting.md +316 -0
  102. package/library/snippets/dax/time-intelligence.md +196 -0
  103. package/library/snippets/dax/user-defined-functions.md +477 -0
  104. package/library/snippets/dax/virtual-tables.md +546 -0
  105. package/library/snippets/excel-formulas/README.md +84 -0
  106. package/library/snippets/excel-formulas/aggregations.md +330 -0
  107. package/library/snippets/excel-formulas/dates-and-times.md +361 -0
  108. package/library/snippets/excel-formulas/dynamic-arrays.md +314 -0
  109. package/library/snippets/excel-formulas/lookups.md +169 -0
  110. package/library/snippets/excel-formulas/text-functions.md +363 -0
  111. package/library/snippets/governance/naming-conventions.md +97 -0
  112. package/library/snippets/governance/review-checklists.md +107 -0
  113. package/library/snippets/power-query/README.md +389 -0
  114. package/library/snippets/power-query/api-integration.md +707 -0
  115. package/library/snippets/power-query/connections.md +434 -0
  116. package/library/snippets/power-query/data-cleaning.md +298 -0
  117. package/library/snippets/power-query/error-handling.md +526 -0
  118. package/library/snippets/power-query/parameters.md +350 -0
  119. package/library/snippets/power-query/performance.md +506 -0
  120. package/library/snippets/power-query/transformations.md +330 -0
  121. package/library/snippets/report-design/accessibility.md +78 -0
  122. package/library/snippets/report-design/chart-selection.md +54 -0
  123. package/library/snippets/report-design/layout-patterns.md +87 -0
  124. package/library/templates/data-models/README.md +93 -0
  125. package/library/templates/data-models/finance-model.md +627 -0
  126. package/library/templates/data-models/retail-star-schema.md +473 -0
  127. package/library/templates/excel/README.md +83 -0
  128. package/library/templates/excel/budget-tracker.md +432 -0
  129. package/library/templates/excel/data-entry-form.md +533 -0
  130. package/library/templates/power-bi/README.md +72 -0
  131. package/library/templates/power-bi/finance-report.md +449 -0
  132. package/library/templates/power-bi/kpi-scorecard.md +461 -0
  133. package/library/templates/power-bi/sales-dashboard.md +281 -0
  134. package/library/themes/excel/README.md +436 -0
  135. package/library/themes/power-bi/README.md +271 -0
  136. package/library/themes/power-bi/accessible.json +307 -0
  137. package/library/themes/power-bi/bi-superpowers-default.json +858 -0
  138. package/library/themes/power-bi/corporate-blue.json +291 -0
  139. package/library/themes/power-bi/dark-mode.json +291 -0
  140. package/library/themes/power-bi/minimal.json +292 -0
  141. package/library/themes/power-bi/print-friendly.json +309 -0
  142. package/package.json +93 -0
  143. package/skills/contributions/SKILL.md +267 -0
  144. package/skills/data-model-design/SKILL.md +470 -0
  145. package/skills/data-modeling/SKILL.md +254 -0
  146. package/skills/data-quality/SKILL.md +664 -0
  147. package/skills/dax/SKILL.md +708 -0
  148. package/skills/dax-doctor/SKILL.md +250 -0
  149. package/skills/dax-udf/SKILL.md +489 -0
  150. package/skills/deployment/SKILL.md +320 -0
  151. package/skills/excel-formulas/SKILL.md +463 -0
  152. package/skills/fabric-scripts/SKILL.md +454 -0
  153. package/skills/fast-standard/SKILL.md +509 -0
  154. package/skills/governance/SKILL.md +205 -0
  155. package/skills/migration-assistant/SKILL.md +292 -0
  156. package/skills/model-documenter/SKILL.md +244 -0
  157. package/skills/pbi-connect/SKILL.md +241 -0
  158. package/skills/power-query/SKILL.md +406 -0
  159. package/skills/project-kickoff/SKILL.md +907 -0
  160. package/skills/query-performance/SKILL.md +480 -0
  161. package/skills/report-design/SKILL.md +207 -0
  162. package/skills/report-layout/SKILL.md +298 -0
  163. package/skills/rls-design/SKILL.md +535 -0
  164. package/skills/semantic-model/SKILL.md +237 -0
  165. package/skills/testing-validation/SKILL.md +643 -0
  166. package/skills/theme-tweaker/SKILL.md +626 -0
  167. package/src/content/base.md +237 -0
  168. package/src/content/mcp-requirements.json +69 -0
  169. package/src/content/routing.md +203 -0
  170. package/src/content/skills/contributions.md +259 -0
  171. package/src/content/skills/data-model-design.md +462 -0
  172. package/src/content/skills/data-modeling.md +246 -0
  173. package/src/content/skills/data-quality.md +656 -0
  174. package/src/content/skills/dax-doctor.md +242 -0
  175. package/src/content/skills/dax-udf.md +481 -0
  176. package/src/content/skills/dax.md +700 -0
  177. package/src/content/skills/deployment.md +312 -0
  178. package/src/content/skills/excel-formulas.md +455 -0
  179. package/src/content/skills/fabric-scripts.md +446 -0
  180. package/src/content/skills/fast-standard.md +501 -0
  181. package/src/content/skills/governance.md +197 -0
  182. package/src/content/skills/migration-assistant.md +284 -0
  183. package/src/content/skills/model-documenter.md +236 -0
  184. package/src/content/skills/pbi-connect.md +233 -0
  185. package/src/content/skills/power-query.md +398 -0
  186. package/src/content/skills/project-kickoff.md +899 -0
  187. package/src/content/skills/query-performance.md +472 -0
  188. package/src/content/skills/report-design.md +199 -0
  189. package/src/content/skills/report-layout.md +290 -0
  190. package/src/content/skills/rls-design.md +527 -0
  191. package/src/content/skills/semantic-model.md +229 -0
  192. package/src/content/skills/testing-validation.md +635 -0
  193. 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