@microsoft/powerbi-modeling-mcp-linux-arm64 0.5.0-beta.13 → 0.5.0-beta.15

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.
@@ -0,0 +1,714 @@
1
+ ---
2
+ name: 'DAX Query Instructions and Examples'
3
+ description: 'Guidelines for writing Power BI DAX queries'
4
+ uriTemplate: 'resource://dax_query_instructions_and_examples'
5
+ ---
6
+ # DAX Query Language Guide
7
+
8
+ ## Overview
9
+
10
+ DAX (Data Analysis Expressions) is a formula language used in Power BI for creating custom calculations and queries. This guide provides comprehensive instructions and examples for writing valid DAX query expressions.
11
+
12
+ ## DAX Query Best Practices
13
+
14
+ When writing DAX queries, follow these recommendations for optimal results:
15
+
16
+ * Include comments for clarity (DAX comments use `//` not `--`)
17
+ * Always include an ORDER BY clause when returning multiple rows
18
+ * Use meaningful variable names to improve readability
19
+ * Define measures with fully qualified names in DEFINE blocks
20
+
21
+ ## DAX Query Syntax Rules
22
+
23
+ ### Query Structure
24
+
25
+ #### DEFINE Block
26
+
27
+ * Use DEFINE at the beginning if the query includes VAR, MEASURE, COLUMN, or TABLE definitions
28
+ * Only use a single DEFINE block per query
29
+ * Separate definitions with new lines (no commas or semicolons)
30
+
31
+ #### Measure Definitions
32
+
33
+ * When defining: ALWAYS fully qualify the measure name including its host table
34
+ * Example: `DEFINE MEASURE 'TableName'[MeasureName] = ...`
35
+ * The host table must exist in the semantic model
36
+ * When using: Refer to the measure by name only, without the table qualifier
37
+ * Example: Use `[MeasureName]` in expressions like `CALCULATE([MeasureName], ...)`
38
+
39
+ #### Ordering Results
40
+
41
+ * ALWAYS include an ORDER BY clause when EVALUATE returns multiple rows
42
+ * Do not use the ORDERBY function to sort the final query result
43
+
44
+ ### CALCULATE and CALCULATETABLE Filter Rules
45
+
46
+ Boolean filters in CALCULATE or CALCULATETABLE have important restrictions:
47
+
48
+ * Cannot directly use a measure or another CALCULATE function
49
+ * Solution: Use a variable to store the result, then reference the variable
50
+ * Cannot reference columns from two different tables
51
+ * When using the IN operator, the table operand must be a table variable, not a table expression
52
+ * Do not assign a boolean filter to a VAR definition
53
+
54
+ ### SUMMARIZECOLUMNS Function
55
+
56
+ **Purpose**: Build summary tables with groupby columns and measure-like extension columns
57
+
58
+ **Parameter Order** (all optional, but must follow this order if used):
59
+
60
+ 1. Groupby columns (can be from one or multiple tables)
61
+ 2. Filters
62
+ 3. Measures or measure-like calculations
63
+
64
+ **Key Rules**:
65
+
66
+ * Use SUMMARIZECOLUMNS as the default for building summary tables with measures
67
+ * Do not use SUMMARIZECOLUMNS without measure-like extension columns
68
+ * Returns only rows where at least one measure value is not BLANK
69
+ * Allows ANY number of measure-like calculations of arbitrary complexity
70
+ * DO NOT use boolean filters with SUMMARIZECOLUMNS
71
+
72
+ **When to Use Alternatives**:
73
+
74
+ * If there are no measures or calculations, use SUMMARIZE instead
75
+
76
+ ### SUMMARIZE Function
77
+
78
+ **Allowed Pattern**:
79
+
80
+ ```dax
81
+ SUMMARIZE(<table expression>, <column1>, …, <columnN>)
82
+ ```
83
+
84
+ **Critical Restrictions**:
85
+
86
+ * NEVER use SUMMARIZE with measure-like expressions
87
+ * ❌ Incorrect: `SUMMARIZE(<table>, <column>, "expr1", <expr1>, …)`
88
+ * ✅ Correct: Use SUMMARIZECOLUMNS for measure calculations
89
+ * Use for extracting distinct combinations of columns only
90
+ * `VALUES('Table'[Column])` is a shortcut for `SUMMARIZE('Table', 'Table'[Column])`
91
+ * When extracting a column from a table variable: `SUMMARIZE(_TableVar, [Column])`
92
+ * Note: `_TableVar[Column]` is not valid syntax
93
+
94
+ **When to Use Alternatives**:
95
+
96
+ * For measure calculations: Use SUMMARIZECOLUMNS
97
+ * For aggregations on table variables: Use GROUPBY
98
+
99
+ ### GROUPBY Function
100
+
101
+ **Purpose**: Perform simple aggregations on table-valued variables at a grouped level
102
+
103
+ **Key Rules**:
104
+
105
+ * Only use GROUPBY with a table-valued variable as the first argument
106
+ * The CURRENTGROUP function is valid ONLY within GROUPBY
107
+ * CURRENTGROUP must not be used elsewhere
108
+
109
+ ### SELECTCOLUMNS Function
110
+
111
+ **Purpose**: Project columns while preserving duplicates or renaming columns
112
+
113
+ **Key Rules**:
114
+
115
+ * Use to preserve duplicate rows (unlike SUMMARIZE which removes them)
116
+ * Use to rename columns for clarity
117
+ * When renaming columns, subsequent expressions (TOPN, ORDER BY) must use the NEW column names
118
+
119
+ **Important**: Include all columns needed for later operations (ORDER BY, FILTER, etc.)
120
+
121
+ ### Table Expressions and Filters
122
+
123
+ * When using table expressions (SELECTCOLUMNS, CALCULATETABLE), include any columns needed later
124
+ * Filters applied to one table can propagate across relationships based on filter direction (unidirectional or bidirectional)
125
+
126
+ ### Set Functions
127
+
128
+ When using INTERSECT, UNION, or EXCEPT:
129
+
130
+ * Both input tables must produce an identical number of columns
131
+
132
+ ### Time Intelligence Functions
133
+
134
+ **DATESINPERIOD Rolling Windows**:
135
+
136
+ * The negative period offset must precisely match the number of periods required
137
+ * Examples:
138
+ * 12-month window: Use -12 (not -11)
139
+ * 3-month window: Use -3 (not -2)
140
+ * This prevents off-by-one errors
141
+
142
+ **Maintaining Clear Date Context**:
143
+
144
+ * Always establish a valid date context for time intelligence calculations
145
+ * Methods:
146
+ * Include groupby columns from the date table, OR
147
+ * Apply filters on date columns
148
+ * Without date context, time intelligence functions cannot determine a "current date" reference
149
+ * When using ROW function with time intelligence, supply external filters through CALCULATETABLE
150
+
151
+ ## Sample Data Model
152
+
153
+ These examples use a simplified hypothetical data model:
154
+
155
+ ```yaml
156
+ Tables:
157
+ - Name: Sales
158
+ Measures:
159
+ - Name: Total Discount
160
+ Type: Decimal
161
+ - Name: Total Amount
162
+ Type: Decimal
163
+ - Name: Total Quantity
164
+ Type: Integer
165
+ Columns:
166
+ - Name: CustomerKey
167
+ Type: Text
168
+ - Name: Order Quantity
169
+ Type: Integer
170
+ - Name: ProductKey
171
+ Type: Text
172
+ - Name: OrderDate
173
+ Type: Date
174
+ - Name: Sales Amount
175
+ Type: Decimal
176
+ - Name: Product
177
+ Measures:
178
+ - Name: Median List Price
179
+ Type: Decimal
180
+ Columns:
181
+ - Name: Category
182
+ Type: Text
183
+ MinValue: Consumer Electronics
184
+ MaxValue: Toys
185
+ - Name: Color
186
+ Type: Text
187
+ MinValue: Beige
188
+ MaxValue: Red
189
+ - Name: List Price
190
+ Description: Retail price of the product
191
+ Type: Decimal
192
+ - Name: Name
193
+ Type: Text
194
+ - Name: ProductKey
195
+ Type: Text
196
+ - Name: Customer
197
+ Columns:
198
+ - Name: CustomerKey
199
+ Type: Text
200
+ - Name: Name
201
+ Type: Text
202
+ - Name: Age
203
+ Type: Integer
204
+ - Name: Calendar
205
+ Columns:
206
+ - Name: Date
207
+ Type: Date
208
+ - Name: Month
209
+ Type: Text
210
+ SortByColumnName: MonthNumberOfYear
211
+ - Name: MonthNumberOfYear
212
+ Type: Integer
213
+ - Name: Year
214
+ Type: Integer
215
+ Active Relationships:
216
+ - PK: 'Product'[ProductKey]
217
+ FK: 'Sales'[ProductKey]
218
+ Unidirectional Filter Propagation: "'Product' filters 'Sales'"
219
+ - PK: 'Customer'[CustomerKey]
220
+ FK: 'Sales'[CustomerKey]
221
+ Unidirectional Filter Propagation: "'Customer' filters 'Sales'"
222
+ - PK: 'Calendar'[Date]
223
+ FK: 'Sales'[OrderDate]
224
+ Unidirectional Filter Propagation: "'Calendar' filters 'Sales'"
225
+ ```
226
+
227
+ ## DAX Query Examples
228
+
229
+ The following examples demonstrate proper DAX query syntax and best practices using the data model defined above.
230
+
231
+ ### Example 1: Time Intelligence with Rolling Averages
232
+
233
+ **Scenario**: Calculate year-to-date total sales and 14-day moving average for red products.
234
+
235
+ ```dax
236
+ // Year-to-date total sales and 14-day moving average of sales for red products.
237
+ EVALUATE
238
+ CALCULATETABLE(
239
+ ROW(
240
+ "Total Sales Amount YTD", TOTALYTD([Total Amount], 'Calendar'[Date]),
241
+ "Total Sales Amount 14-Day MA", AVERAGEX(DATESINPERIOD('Calendar'[Date], MAX('Calendar'[Date]), -14, DAY), [Total Amount]) // Note that the number_of_intervals parameter must be -14 instead of -13.
242
+ ),
243
+ 'Product'[Color] == "Red",
244
+ TREATAS({ MAX('Sales'[OrderDate]) }, 'Calendar'[Date]) // Establish a reference date for TI functions TOTALYTD and DATESINPERIOD.
245
+ )
246
+ ```
247
+
248
+ **Key Concepts**:
249
+
250
+ * Uses CALCULATETABLE with ROW to establish date context for time intelligence functions
251
+ * TREATAS establishes a clear "current date" reference for time intelligence
252
+ * DATESINPERIOD uses -14 (not -13) for a proper 14-day window
253
+
254
+ ### Example 2: Multi-Level Aggregation with GROUPBY
255
+
256
+ **Scenario**: Calculate average, minimum, and maximum monthly sales quantity by year for Consumer Electronics before 2023.
257
+
258
+ ```dax
259
+ DEFINE
260
+ // Filters for products in Consumer Electronics category
261
+ VAR _Filter1 = TREATAS(
262
+ {
263
+ "Consumer Electronics"
264
+ },
265
+ 'Product'[Category]
266
+ )
267
+ // Filters to years before 2023
268
+ VAR _Filter2 = FILTER(
269
+ ALL('Calendar'[Year]),
270
+ 'Calendar'[Year] < 2023
271
+ )
272
+
273
+ // Quantity filtered to Consumer Electronics products for years before 2023, grouped by month
274
+ VAR _SummaryTable = SUMMARIZECOLUMNS(
275
+ 'Calendar'[Year],
276
+ 'Calendar'[Month],
277
+ // [Month] is a required groupby column.
278
+ // A query always sorts by required groupby columns.
279
+ // [MonthNumberOfYear] is the orderby column for [Month].
280
+ // Also include [MonthNumberOfYear] to be used in the ORDER BY clause.
281
+ 'Calendar'[MonthNumberOfYear],
282
+ _Filter1,
283
+ _Filter2,
284
+ "Monthly Quantity", [Total Quantity]
285
+ )
286
+ // Aggregate the summarized monthly data by year and month to derive average, minimum, and maximum monthly quantities.
287
+ EVALUATE
288
+ // GROUPBY function is used to summarize intermediate tables.
289
+ GROUPBY(
290
+ _SummaryTable,
291
+ 'Calendar'[Year],
292
+ 'Calendar'[Month],
293
+ 'Calendar'[MonthNumberOfYear],
294
+ "Avg Monthly Quantity",
295
+ AVERAGEX(
296
+ CURRENTGROUP(), // must be used inside GROUPBY function
297
+ [Monthly Quantity]
298
+ ),
299
+ "Min Monthly Quantity",
300
+ MINX(
301
+ CURRENTGROUP(), // must be used inside GROUPBY function
302
+ [Monthly Quantity]
303
+ ),
304
+ "Max Monthly Quantity",
305
+ MAXX(
306
+ CURRENTGROUP(), // must be used inside GROUPBY function
307
+ [Monthly Quantity]
308
+ )
309
+ )
310
+ ORDER BY
311
+ 'Calendar'[Year] ASC,
312
+ // [MonthNumberOfYear] is the orderby column for [Month].
313
+ // ORDER BY [MonthNumberOfYear] instead of [Month].
314
+ 'Calendar'[MonthNumberOfYear] ASC
315
+ ```
316
+
317
+ **Key Concepts**:
318
+
319
+ * SUMMARIZECOLUMNS creates initial summary with measures
320
+ * GROUPBY performs secondary aggregation on the summary table
321
+ * CURRENTGROUP is used exclusively within GROUPBY
322
+ * Includes MonthNumberOfYear for proper sorting
323
+
324
+ ### Example 3: Filtering with Measures Using Variables
325
+
326
+ **Scenario**: Find products with total sales over $1 million that are red or black.
327
+
328
+ ```dax
329
+ DEFINE
330
+ // Red or Black product filter
331
+ VAR _Filter = TREATAS(
332
+ {
333
+ "Red",
334
+ "Black"
335
+ },
336
+ 'Product'[Color]
337
+ )
338
+ // Sales of Red or Black products
339
+ VAR _SummaryTable = SUMMARIZECOLUMNS(
340
+ 'Product'[Name],
341
+ _Filter,
342
+ "Total Sales", [Total Amount]
343
+ )
344
+
345
+ // Products with total sales above $1,000,000
346
+ EVALUATE
347
+ SELECTCOLUMNS(
348
+ FILTER(
349
+ _SummaryTable,
350
+ [Total Sales] > 1000000
351
+ ),
352
+ 'Product'[Name]
353
+ )
354
+ ORDER BY
355
+ 'Product'[Name] ASC
356
+ ```
357
+
358
+ **Key Concepts**:
359
+
360
+ * TREATAS creates a filter from a list of values
361
+ * SUMMARIZECOLUMNS builds summary with measure
362
+ * FILTER applied to summary table variable
363
+ * SELECTCOLUMNS projects only needed columns
364
+
365
+ ### Example 4: SUMMARIZE for Distinct Values (No Duplicates)
366
+
367
+ **Scenario**: Get unique combinations of color, category, and product key for products sold in 2022.
368
+
369
+ ```dax
370
+ // Product color, category and key for products sold in 2022
371
+ EVALUATE
372
+ CALCULATETABLE(
373
+ // SUMMARIZE is used to remove duplicate rows
374
+ SUMMARIZE(
375
+ 'Sales',
376
+ 'Product'[Color],
377
+ 'Product'[Category],
378
+ 'Sales'[ProductKey]
379
+ ),
380
+ 'Calendar'[Year] == 2022
381
+ )
382
+ ORDER BY
383
+ 'Product'[Color] ASC,
384
+ 'Product'[Category] ASC,
385
+ 'Sales'[ProductKey] ASC
386
+ ```
387
+
388
+ **Key Concepts**:
389
+
390
+ * SUMMARIZE removes duplicate rows
391
+ * CALCULATETABLE applies year filter
392
+ * Related table columns accessed via relationships
393
+
394
+ ### Example 5: SELECTCOLUMNS for Preserving Duplicates
395
+
396
+ **Scenario**: Get color, category, and product key for products sold in 2022, keeping all duplicate rows.
397
+
398
+ ```dax
399
+ // Product color, category and key for products sold in 2022
400
+ EVALUATE
401
+ CALCULATETABLE(
402
+ SELECTCOLUMNS(
403
+ 'Sales',
404
+ "Color",
405
+ RELATED('Product'[Color]),
406
+ "Category",
407
+ RELATED('Product'[Category]),
408
+ 'Sales'[ProductKey]
409
+ ),
410
+ 'Calendar'[Year] == 2022
411
+ )
412
+ ORDER BY
413
+ [Color] ASC,
414
+ [Category] ASC,
415
+ 'Sales'[ProductKey] ASC
416
+ ```
417
+
418
+ **Key Concepts**:
419
+
420
+ * SELECTCOLUMNS preserves duplicate rows (unlike SUMMARIZE)
421
+ * Column renaming requires using new names in ORDER BY
422
+ * RELATED accesses columns from related tables
423
+
424
+ ### Example 6: Finding Products with No Sales
425
+
426
+ **Scenario**: Identify products that have never been sold.
427
+
428
+ ```dax
429
+ DEFINE
430
+ // Sale row count
431
+ MEASURE 'Sales'[Row Count] = COUNTROWS()
432
+
433
+ // Products with no sales
434
+ EVALUATE
435
+ FILTER(
436
+ 'Product',
437
+ ISBLANK([Row Count])
438
+ )
439
+ ORDER BY
440
+ 'Product'[Name] ASC,
441
+ 'Product'[ProductKey] ASC
442
+ ```
443
+
444
+ **Key Concepts**:
445
+
446
+ * Defines a measure for row counting
447
+ * FILTER with ISBLANK identifies products with no related sales
448
+ * Filter context propagates from Product to Sales table
449
+
450
+ ### Example 7: Using Variables to Store Measure Results
451
+
452
+ **Scenario**: Find products with list prices above the median.
453
+
454
+ **Solution 1 - Using CALCULATETABLE**:
455
+
456
+ ```dax
457
+ DEFINE
458
+ // Calculate the value of the [Median List Price] measure and store the result in a variable.
459
+ VAR _MedianListPrice = [Median List Price]
460
+
461
+ // Products with list price over the median.
462
+ EVALUATE
463
+ CALCULATETABLE(
464
+ VALUES('Product'[Name]),
465
+ 'Product'[List Price] > _MedianListPrice // boolean filter uses variable instead of measure directly
466
+ )
467
+ ORDER BY
468
+ 'Product'[Name] ASC
469
+ ```
470
+
471
+ **Solution 2 - Using FILTER**:
472
+
473
+ ```dax
474
+ DEFINE
475
+ // Calculate the value of the [Median List Price] measure and store the result in a variable.
476
+ VAR _MedianListPrice = [Median List Price]
477
+
478
+ // Products with list price over the median.
479
+ EVALUATE
480
+ SELECTCOLUMNS(
481
+ FILTER(
482
+ VALUES('Product'),
483
+ 'Product'[List Price] > _MedianListPrice // Use variable instead of measure reference to ensure the median list price is calculated across all products
484
+ ),
485
+ 'Product'[Name]
486
+ )
487
+ ORDER BY
488
+ 'Product'[Name] ASC
489
+ ```
490
+
491
+ **Key Concepts**:
492
+
493
+ * Variables store measure results for use in boolean filters
494
+ * Cannot use measures directly in CALCULATE boolean filters
495
+ * Both approaches yield the same result with different syntax
496
+
497
+ ### Example 8: Using Table Variables as Filters
498
+
499
+ **Scenario**: Find the product with highest demand since 2020 and get its sale dates.
500
+
501
+ ```dax
502
+ DEFINE
503
+ // To make query more readable, a filter can be defined separately.
504
+ VAR _Filter = FILTER(
505
+ ALL('Calendar'[Year]),
506
+ 'Calendar'[Year] >= 2020
507
+ )
508
+ // Get the product with the maximum Total Quantity
509
+ VAR _TopProduct = TOPN(
510
+ 1,
511
+ SUMMARIZECOLUMNS(
512
+ 'Product'[ProductKey],
513
+ _Filter,
514
+ "Total Quantity", [Total Quantity]
515
+ ),
516
+ [Total Quantity],
517
+ DESC
518
+ )
519
+
520
+ // Name and order date for sales of the top product
521
+ EVALUATE
522
+ SELECTCOLUMNS(
523
+ CALCULATETABLE(
524
+ 'Sales',
525
+ // Use table-valued variable _TopProduct directly as a filter.
526
+ // No need to extract the 'Product'[ProductKey] first.
527
+ // Calculated column [Total Quantity] has no effect in the filter context.
528
+ _TopProduct
529
+ ),
530
+ "Product Name",
531
+ RELATED('Product'[Name]),
532
+ 'Sales'[OrderDate]
533
+ )
534
+ ORDER BY
535
+ [Product Name] ASC,
536
+ 'Sales'[OrderDate] ASC
537
+ ```
538
+
539
+ **Key Concepts**:
540
+
541
+ * TOPN identifies the product with highest total quantity
542
+ * Table variables can be used directly as filters in CALCULATETABLE
543
+ * Calculated columns in table variables don't affect filter context
544
+
545
+ ### Example 9: Calculating Averages on Filtered Subsets
546
+
547
+ **Scenario**: Calculate the average list price of products sold in 2022.
548
+
549
+ ```dax
550
+ DEFINE
551
+ // Distinct products sold in 2022
552
+ VAR _ProductsSold2022 = CALCULATETABLE(
553
+ SUMMARIZE(
554
+ 'Sales',
555
+ 'Product'[ProductKey]
556
+ ),
557
+ 'Calendar'[Year] == 2022
558
+ )
559
+ EVALUATE
560
+ ROW(
561
+ "Average List Price of Products Sold in 2022",
562
+ CALCULATE(
563
+ AVERAGE('Product'[List Price]),
564
+ _ProductsSold2022 // Apply table-valued variable as a filter
565
+ )
566
+ )
567
+ ```
568
+
569
+ **Key Concepts**:
570
+
571
+ * SUMMARIZE extracts distinct products sold in 2022
572
+ * ROW returns a single-row result
573
+ * Table variable applied as filter in CALCULATE
574
+
575
+ ### Example 10: Column Renaming with SELECTCOLUMNS
576
+
577
+ **Scenario**: Get products sold and customers, sorted by renamed column names.
578
+
579
+ ```dax
580
+ // Sorted product and customer names for all sales
581
+ DEFINE
582
+ VAR _UniqueProductCustomerPairs = SUMMARIZE(
583
+ 'Sales',
584
+ 'Product'[Name],
585
+ 'Customer'[Name]
586
+ )
587
+ EVALUATE
588
+ SELECTCOLUMNS(
589
+ _UniqueProductCustomerPairs,
590
+ "Product Name", // New name for the 'Product'[Name] column
591
+ 'Product'[Name],
592
+ "Customer Name", // New name for the 'Customer'[Name] column
593
+ 'Customer'[Name]
594
+ )
595
+ // ORDER BY needs to use the renamed column names
596
+ ORDER BY
597
+ [Product Name] ASC, // Use the new column name assigned by SELECTCOLUMNS instead of the original column name 'Product'[Name]
598
+ [Customer Name] ASC // Use the new column name assigned by SELECTCOLUMNS instead of the original column name 'Customer'[Name]
599
+ ```
600
+
601
+ **Key Concepts**:
602
+
603
+ * SELECTCOLUMNS renames columns for clarity
604
+ * ORDER BY must reference the NEW column names, not original names
605
+ * SUMMARIZE removes duplicate product-customer pairs
606
+
607
+ ### Example 11: Multiple Filters with TREATAS
608
+
609
+ **Scenario**: For the three oldest customers, show total discounts by year for the last three years.
610
+
611
+ ```dax
612
+ DEFINE
613
+ VAR _OldestThreeCustomers = TOPN(
614
+ 3,
615
+ 'Customer',
616
+ 'Customer'[Age],
617
+ DESC
618
+ )
619
+ // Determine the last year based on actual sales dates.
620
+ // Avoid using the last year in the 'Calendar' table, as it may include future dates without sales.
621
+ VAR _LastYear = YEAR(MAX('Sales'[OrderDate]))
622
+ EVALUATE
623
+ SUMMARIZECOLUMNS(
624
+ 'Customer'[Name],
625
+ 'Calendar'[Year],
626
+ TREATAS({_LastYear, _LastYear - 1, _LastYear - 2}, 'Calendar'[Year]),
627
+ _OldestThreeCustomers, // Apply table-valued variable as filter.
628
+ "Total Discount",
629
+ [Total Discount]
630
+ )
631
+ ORDER BY
632
+ 'Customer'[Name] ASC,
633
+ 'Calendar'[Year] ASC
634
+ ```
635
+
636
+ **Key Concepts**:
637
+
638
+ * TOPN selects top 3 customers by age
639
+ * TREATAS creates filter from calculated year values
640
+ * Determines last year from actual sales data, not calendar table
641
+
642
+ ### Example 12: Filtering Aggregated Results
643
+
644
+ **Scenario**: For each customer, show products purchased at least three times with purchase count.
645
+
646
+ ```dax
647
+ DEFINE
648
+ MEASURE 'Sales'[Purchase Count] = COUNTROWS()
649
+ VAR _SummaryTable = SUMMARIZECOLUMNS(
650
+ 'Customer'[Name],
651
+ 'Product'[Name],
652
+ "Purchase Count", [Purchase Count]
653
+ )
654
+ EVALUATE
655
+ FILTER(_SummaryTable, [Purchase Count] >= 3)
656
+ ORDER BY
657
+ 'Customer'[Name] ASC,
658
+ 'Product'[Name] ASC
659
+ ```
660
+
661
+ **Key Concepts**:
662
+
663
+ * Defines measure for counting purchases
664
+ * SUMMARIZECOLUMNS creates summary with measure
665
+ * FILTER applied to summary table variable
666
+ * Simple and efficient two-step pattern
667
+
668
+ ### Example 13: Calculated Columns in DEFINE
669
+
670
+ **Scenario**: Categorize products by price (above/below median) and show max/min quantities per category.
671
+
672
+ ```dax
673
+ DEFINE
674
+ // define a new column so that it can be used in SUMMARIZECOLUMNS
675
+ COLUMN 'Product'[Price Group] =
676
+ VAR _MedianListPrice = [Median List Price]
677
+ RETURN
678
+ IF(
679
+ 'Product'[List Price] > _MedianListPrice,
680
+ "High Priced",
681
+ "Low Priced"
682
+ )
683
+ MEASURE 'Sales'[Max Quantity] = MAX('Sales'[Order Quantity])
684
+ MEASURE 'Sales'[Min Quantity] = MIN('Sales'[Order Quantity])
685
+ EVALUATE
686
+ SUMMARIZECOLUMNS(
687
+ 'Product'[Price Group],
688
+ "Max Quantity",
689
+ [Max Quantity],
690
+ "Min Quantity",
691
+ [Min Quantity]
692
+ )
693
+ ORDER BY 'Product'[Price Group] ASC
694
+ ```
695
+
696
+ **Key Concepts**:
697
+
698
+ * COLUMN defines a calculated column in DEFINE block
699
+ * Calculated columns can be used as groupby columns in SUMMARIZECOLUMNS
700
+ * Multiple measures defined and used in same query
701
+ * IF expression for conditional logic
702
+
703
+ ## Summary
704
+
705
+ This guide covers the essential rules and patterns for writing valid DAX queries. Key takeaways:
706
+
707
+ 1. **Always use ORDER BY** when returning multiple rows
708
+ 2. **Store measure results in variables** before using in boolean filters
709
+ 3. **Choose the right function**: SUMMARIZECOLUMNS for measures, SUMMARIZE for distinct values, GROUPBY for table variables
710
+ 4. **Establish date context** for time intelligence functions
711
+ 5. **Use renamed columns** in ORDER BY after SELECTCOLUMNS
712
+ 6. **Leverage table variables** as filters for cleaner, more maintainable code
713
+
714
+ Practice these patterns to write efficient, readable DAX queries that follow best practices.