8. Adding Columns: Power Query Add Column Tab and DAX Calculated Columns
8.12 Calculated Columns vs Measures in Depth
Start with a problem
Salman builds a fictional Nagpur dark-store report for class. He needs Net Sales by City and % of orders under 10 minutes. He creates two calculated columns: Net Amount and Profit %. Net Amount + SUM works. Profit % averaged across cities is wrong. Interviewers ask him this every time — this section is the full answer.
Explain simply
- Calculated column: new column; DAX runs once per row at refresh; result is stored.
- Measure: named formula; not stored; runs at query time in the filter context of each visual cell.
- Same formula can give different results in the two places because row context ≠ filter context (Module 13.3).
Show the data
Fictional sample (src/practice/column_vs_measure_orders.csv):
| Order ID | City | Amount | Discount | Delivery Time Mins | Order Status |
|---|---|---|---|---|---|
| 201 | Pune | 500 | 50 | 8 | Delivered |
| 202 | Nashik | 300 | 0 | 12 | Delivered |
| 203 | Pune | 700 | 100 | 9 | Delivered |
| 204 | Kolhapur | 400 | 0 | 18 | Delivered |
| 205 | Nashik | 0 | 0 | Cancelled |
About this data: This table uses columns: Order ID, City, Amount, Discount, Delivery Time Mins, Order Status. This table uses columns: City, Net Sales (₹), % Under 10 (delivered). This table uses columns: Calculated column, Measure. This table uses columns: Wrong result, Reason, Correction. Values shown are the lesson examples already on this page (not a new dataset).
Hand: Net all = 1750 (1,900 − 50 − 100); under-10 among delivered = 2 of 4 = 50%.
Demonstrate
- Load the practice CSV.
- Optional column:
Net Amount = Orders[Amount] - Orders[Discount]. - Measure:
Net Sales (A) = SUM(Orders[Net Amount])or skip the column and useNet Sales (B) = SUMX(Orders, Orders[Amount] - Orders[Discount]). - Measure for ratio (never as a column to average):
% Under 10 Mins =
DIVIDE(
CALCULATE(DISTINCTCOUNT(Orders[Order ID]), Orders[Order Status] = "Delivered", Orders[Delivery Time Mins] <= 10),
CALCULATE(DISTINCTCOUNT(Orders[Order ID]), Orders[Order Status] = "Delivered")
)
- Matrix: City × Net Sales (B) and % Under 10 Mins. Confirm total % is 50%, not the average of city percentages.
The same logic implemented both ways
Goal: show Net Sales (Amount − Discount) by City.
-- Option A: calculated column + sum
Net Amount = Orders[Amount] - Orders[Discount]
Net Sales (A) = SUM(Orders[Net Amount])
-- Option B: measure only (iterator), nothing stored
Net Sales (B) = SUMX(Orders, Orders[Amount] - Orders[Discount])
Both return the same number for every city. Option B uses no extra memory. Option A helps if you also filter or bucket by net per line.
Goal: % of orders delivered within 10 minutes — must be a measure (see formula above). A ratio stored as a column is wrong when summed or averaged across cities.
Show the result
Your result should look like this (fictional sample):
| City | Net Sales (₹) | % Under 10 (delivered) |
|---|---|---|
| Pune | 1,050 | 100% (2/2) |
| Nashik | 300 | 0% (0/1 delivered with time) |
| Kolhapur | 400 | 0% (0/1) |
| Total | 1,750 | 50% (2/4) |
Explain what happened
Columns materialise row values. Measures recompute with whatever filters the cell has. Averaging percentages ignores different denominators; DIVIDE on totals uses the correct combined numerator and denominator.
Explore further
| Calculated column | Measure | |
|---|---|---|
| Result | Value for each row | One value for current filter context |
| When | At refresh | At query time |
| Stored? | Yes — RAM / file size | No — formula only |
| Context | Row (use CALCULATE for transition) | Filter context |
| Slicers change value? | No (fixed per row) | Yes |
| On slicer / axis | Yes | No |
| Typical use | Bands, flags, keys, sort | Totals, ratios, KPIs, YTD |
Decision rule
Ask: "Do I want to slice/filter/group by this value, or show/aggregate it?" Slice by → column (preferably Power Query). Show/aggregate → measure.
Fix common mistakes
| Wrong result | Reason | Correction |
|---|---|---|
| Profit % total looks "off" | Averaged column percentages | Measure with DIVIDE of totals |
| % Under 10 shows 75% instead of 50% | Cancelled order 205 has a blank delivery time, and in DAX BLANK <= 10 is TRUE | Add Orders[Order Status] = "Delivered" to the numerator as well |
| "Today" column never moves mid-day | Calculated at refresh | Measure with TODAY() / UTCNOW() as needed |
| Model bloated | Many fact calculated columns | Prefer PQ + measures |
Practise independently
Practice task
Implement Net Sales (B) only and % Under 10 Mins. Match the table above. File: src/practice/column_vs_measure_orders.csv.
Hints
- Cancelled order 205 has blank delivery time — exclude via Order Status = Delivered in the denominator.
- Answers in
src/practice/ANSWERS.md.
Solution
Net Sales total 1750; % Under 10 = 2/4 = 50%. Pune net 1050 with both delivered orders under 10 minutes.
Think like a professional
Memory: high-cardinality text columns on large facts hurt VertiPaq. Governance: one official measure for each KPI name. Interview: draw the decision table; say ratios and time intelligence are measures.
Ravindra Bagale's Tip
Friends, don't make a mistake here: the classic mistake is creating a "Profit %" calculated column and then averaging it in a visual, which gives a wrong percentage. Ratios must be measures: DIVIDE([Gross Profit], [Total Sales]). Also remember that a calculated column storing "today" is fixed at refresh time, not live. Keep this in mind!
Ravindra Bagale's Tip – मराठी
मित्रांनो, इथे चूक करू नका: classic चूक म्हणजे "Profit %" calculated column बनवणं आणि मग visual मध्ये त्याचं average घेणं, त्याने चुकीची percentage येते. Ratios measures च असायला हव्यात: DIVIDE([Gross Profit], [Total Sales]). आणि लक्षात ठेवा, "today" साठवणारा calculated column refresh च्या वेळी fixed होतो, live नसतो. लक्षात ठेवा!
Ravindra Bagale's Tip – हिंदी
दोस्तों, यहाँ गलती मत करना: classic गलती है "Profit %" calculated column बनाना और फिर visual में उसका average लेना, जिससे गलत percentage आती है. Ratios measures ही होने चाहिए: DIVIDE([Gross Profit], [Total Sales]). और याद रखो, "today" रखने वाला calculated column refresh के समय fixed हो जाता है, live नहीं रहता. ध्यान रखना!
Loading DAX practice tasks...
- Line revenue column (Easy): Calculated column on Sales: Line Revenue = Cups × Price.
- Revenue as a measure (Easy): Measure: Revenue Total = Cups × Price added up over Sales.
- City on each sale (Easy): Calculated column on Sales: City = the City of the shop.
- Revenue per shop city (Easy): Shown per Shop City. Measure: City Revenue that simply reuses the Revenue measure.
- Big order flag (Medium): Calculated column on Sales: Big = "Yes" when Cups is 12 or more, else "No".
- Count of big orders (Medium): Shown per Item. Measure: Big Orders = number of Sales rows with Cups 12 or more.
- Average cups per shop (Medium): Shown per ShopName. Measure: Avg Cups = average of Cups.
- Shop revenue column (Hard): Calculated column on Shop: Shop Revenue = Cups × Price added over the sales of that shop.
- Share of all revenue (Hard): Shown per Shop City. Measure: Share = [Revenue] ÷ revenue of all shops.
- Cash sales per barista (Hard): Shown per Barista. Measure: Cash Lines = number of Sales rows paid in Cash.
Revision card
Interview card