One date table, three dates
Fabrikam's Sales fact stores OrderDate, ShipDate and DueDate as date keys. The model has a single Date table, marked as the date table, related to Sales[OrderDate]. Relationships to ShipDate and DueDate exist but are inactive. Report A has a matrix with Date[Month] on rows and two measures: Ordered Qty = SUM(Sales[Qty]) and Shipped Qty = CALCULATE(SUM(Sales[Qty]), USERELATIONSHIP(Sales[ShipDate], 'Date'[Date])). A separate card counts open orders, defined as lines where ShipDate is blank; it shows a number until a date slicer is applied, then goes blank. Report B needs order month on rows and ship month on columns of the same matrix. Shipments lag orders by two to nine days.
1. A slicer on 'Date' is set to March 2024. Explain what Shipped Qty returns, and why the open-orders card goes blank once the slicer is applied.
Read the worked answer
Shipped Qty evaluates with the ShipDate relationship substituted for the active OrderDate one, for the duration of that CALCULATE. The March 2024 filter on 'Date' therefore propagates into the fact through ShipDate, not OrderDate, and the measure returns the quantity of lines shipped in March - a different population from March orders. The inactive relationship alone does nothing: filters never propagate through it, so any measure that does not name USERELATIONSHIP silently reports by order date. That asymmetry is the thing to document.
Open orders are lines with a blank ShipDate. A blank key matches no row in 'Date', so under the ship-date relationship those lines fall outside every date selection and the slicer eliminates them. The card was only correct when no date filter existed. A relationship cannot express not yet shipped.
Options:
- Treat open orders as an as-of snapshot:
CALCULATE(COUNTROWS(Sales), Sales[ShipStatus] = "Open", REMOVEFILTERS('Date'))and put the as-of timestamp in the title. - Give unshipped lines a sentinel ship date and an Unshipped row in the date table - acceptable but pollutes time intelligence if that table is marked as the date table.
- Model open orders as their own snapshot fact, which is the cleanest if the business reports on it daily.
Whichever you pick, state on the page that the number is deliberately not affected by the date slicer.
2. Report B needs order month on rows and ship month on columns in one matrix. Explain why the current model cannot do it and what you change.
Read the worked answer
Row and column headers are both filters on 'Date'. One table instance can only supply both axes from the same columns, so the matrix would show order month against order month. USERELATIONSHIP cannot fix this: it swaps the relationship for a measure's entire evaluation, not for one axis.
Fix: add a second date table (ShipDate) from the same calendar query and relate it to Sales[ShipDate]. Leave the OrderDate relationship active between 'Date' and Sales[OrderDate]. Each axis then has its own filter path:
Date[Month] (rows) -> Sales[OrderDate]
ShipDate[Month] (columns) -> Sales[ShipDate]
measure: SUM(Sales[Qty])
Guardrails:
- Keep both relationships single-direction. Ambiguity appears if you also relate the two date tables to each other or add bidirectional filters - two paths from one dimension to one fact are tolerated, a loop is not.
- Name the tables so nobody confuses them, hide all key columns, and hide the copy from report view if it is only needed for this visual.
- Keep time intelligence (
TOTALYTD,SAMEPERIODLASTYEAR,DATEADD) on the primary'Date'table, which stays marked as the date table. - Remove USERELATIONSHIP from measures that now rely on the
ShipDatetable, or you will apply the role twice.
Cost is small - three years of dates is about 1,100 rows - but every new date role adds a table someone will eventually pick by mistake.
3. A colleague wants the ShipDate relationship to be active, with USERELATIONSHIP used for the order measures instead. Assess that proposal.
Read the worked answer
It is mechanically possible and the measures would still compute correctly, but it silently changes the default for everything that does not explicitly override it:
- Every visual, page, report and dashboard filter using
'Date'now filters on ship date. - Calculated columns and calculated tables cannot use USERELATIONSHIP, so any column needing order-date context becomes impossible or wrong.
- Time intelligence over
'Date'(TOTALYTD,SAMEPERIODLASTYEAR) now traces ship dates, changing published numbers without any visible edit. - Existing reports and Excel pivots keep working and return different values.
Decision rule: make active the relationship used by the majority of measures and by the field users drop on the canvas - here order date - and wrap the minority in USERELATIONSHIP. Shipments lag orders by two to nine days, so the two populations differ enough that a silent swap would be noticed only through a reconciliation nobody runs.
Whatever you choose, record it in the model documentation and name the inactive column explicitly. The failure mode is not an error; it is a plausible but wrong number.