The Power BI Fellowship

Advanced Year 3 – 5

At this level the questions are "why is it slow", "how do 40 reports share one model without breaking", and "how do we ship changes safely". Tools: DAX Studio, Tabular Editor 2 or 3, VertiPaq Analyzer, a Fabric trial, Git.

Performance: VertiPaq, query plans, aggregations, incremental refresh

Data: FactSales WebEvents
Why it matters
Slow reports stop being used, and a model that is too big costs real capacity money every hour.
Typical production failure
An executive page takes 18 seconds every Monday because one measure iterates the whole fact table per cell.
When to use it
Measure first: Performance Analyzer, DAX query view and Server Timings tell you whether the time is in visuals, the formula engine or the storage engine.
When not to
Optimising without a baseline. Every change needs a before and after number.

Assignments

Make a big fact and measure it

A · Guided 40 min · uses FactSales
  1. In Power Query, reference FactSales and cross-join it with a 1,000-row list (List.Numbers) to get 100,000 rows; add a random offset to dates. Name it FactSalesBig.
  2. Open DAX Studio → Advanced → View Metrics (VertiPaq Analyzer). Record total model size and the top 3 columns by size.
  3. Change SalesKey to not loaded (remove it), and change UnitPrice from Decimal to Fixed Decimal. Re-run metrics. Record the size difference.
  4. Split OrderDate/ShipDate into Date only (remove time). Record again.
Expected result: A table with before/after sizes. The high-cardinality key column dominates size; removing it saves the most.

Read a query plan

A · Guided 40 min · uses FactSalesBig
  1. Write the slow version: Customers Over 500 Slow = COUNTROWS(FILTER(FactSalesBig, [Net Sales] > 500)).
  2. In DAX Studio, Server Timings on, run a matrix query using it. Record FE ms, SE ms, number of SE queries, and whether CallbackDataID appears.
  3. Rewrite: COUNTROWS(FILTER(VALUES(DimCustomer[CustomerKey]), [Net Sales] > 500)). Re-run and compare.
  4. Rewrite once more with SUMMARIZE + a filter on a pre-computed column; compare.
Expected result: Version 1 shows one SE query per row or a CallbackDataID; version 2 is 10× fewer SE queries.

Aggregation table

A · Guided 40 min · uses FactSalesBig
  1. Create AggSalesMonthCat in Power Query: group FactSalesBig by YearMonth, Category, RegionKey with Sum Quantity, Sum Amount, Count rows.
  2. Set FactSalesBig to DirectQuery (or keep Import and just practise the mapping), AggSalesMonthCat to Import. Manage aggregations: map each column.
  3. In DAX Studio, run a query by Category and check the "Aggregation hit" in the query plan event; then run by ProductName and confirm it misses.
Expected result: One query hits the agg (Category grain), one misses (Product grain).

Incremental refresh end to end

A · Guided 40 min · uses FactSalesBig via SQL or a partitioned CSV folder
  1. RangeStart/RangeEnd parameters; filter step that folds.
  2. Incremental refresh policy: archive 2 years, refresh 3 days, detect data changes on a ModifiedDate column if you have one.
  3. Publish, refresh, then in SSMS or Tabular Editor via XMLA endpoint (needs capacity/PPU) look at the partitions created.
  4. Simulate a late-arriving fact and confirm it is not picked up outside the refresh window. Then fix with a longer window.
Expected result: Partition list shows year, quarter, month and day partitions.

Storage engine friendly DAX

A · Guided 30 min · uses WebEvents
  1. Write Sessions With Purchase = CALCULATE(DISTINCTCOUNT(WebEvents[SessionID]), WebEvents[EventType] = "Purchase").
  2. Write Conversion Rate = DIVIDE([Sessions With Purchase], DISTINCTCOUNT(WebEvents[SessionID])).
  3. Write Avg Session Minutes two ways: (a) AVERAGEX(VALUES(SessionID), DATEDIFF(CALCULATE(MIN(EventTime)), CALCULATE(MAX(EventTime)), MINUTE)) and (b) with a calculated column SessionStart via EARLIER, then a measure. Compare the query plan cost and model size.
Expected result: You can articulate when to pay the cost at refresh (column) versus at query (measure).

Interview questions

  • How does VertiPaq compress data, and what is the single biggest lever?
  • Formula engine vs storage engine: what runs where?
  • What is a CallbackDataID and why is it bad?
  • Aggregations: when do they not help?
  • Incremental refresh: what happens on the first refresh after publishing, and how do you avoid a timeout?
  • Give three model-level performance rules you enforce in code review.

Assessment

Which change reduces model size most?

  • Rename columns
  • Remove a unique integer key column from a 100k-row fact
  • Change text to uppercase
  • Add a hierarchy

FILTER(FactSales, [Net Sales] > 500) versus FILTER(VALUES(DimCustomer[CustomerKey]), [Net Sales] > 500):

  • Same cost
  • First is far more expensive due to per-row context transition
  • Second is more expensive
  • Both are storage-engine only

Detect data changes in incremental refresh requires:

  • A date column
  • A datetime column such as LastModified that folds
  • Nothing
  • A primary key

Produce a VertiPaq Analyzer before/after table for FactSalesBig with at least three optimisations and the % reduction of each.

Key removal, Fixed Decimal, date/time split, removing unused columns.

Advanced DAX: calculation groups, TREATAS, semi-additive, virtual tables

Data: FactSales FactInventory FactBudget ExchangeRates
Why it matters
Advanced DAX patterns are how one certified model answers hundreds of questions without hundreds of near-duplicate measures.
Typical production failure
A semi-additive stock measure sums weekly snapshots and inventory looks ten times larger than it is.
When to use it
Calculation groups for repeated transformations (time intelligence, currency); window functions for running totals and comparisons.
When not to
Clever DAX that nobody else on the team can maintain. Prefer visual calculations for one-off visual math.

Assignments

Semi-additive inventory

A · Guided 30 min · uses FactInventory, Dates
  1. Stock On Hand = CALCULATE(SUM(FactInventory[QuantityOnHand]), LASTDATE(FactInventory[SnapshotDate])) — put it in a matrix by MonthName. Notice it uses the last snapshot date within each month.
  2. Now the problem: LASTDATE over Dates gives blank for days with no snapshot. Rewrite with LASTNONBLANK: CALCULATE(SUM(QuantityOnHand), LASTNONBLANK(Dates[Date], CALCULATE(SUM(FactInventory[QuantityOnHand])))).
  3. Add Below Reorder = COUNTROWS(FILTER(VALUES(DimProduct[ProductKey]), [Stock On Hand] < CALCULATE(MAX(FactInventory[ReorderPoint])))).
Expected result: Matrix by month shows the last week's level, not a sum. Below Reorder flags Hydro Filter Bottle at the end of January and Ridge Runner Boots at the end of February (both under their reorder point of 30); nothing is below reorder at the end of March.

Calculation group for time intelligence

A · Guided 40 min · uses FactSales, Dates
  1. Tabular Editor 2 → New Calculation Group "Time Calc" with items: Current = SELECTEDMEASURE(), PM = CALCULATE(SELECTEDMEASURE(), DATEADD(Dates[Date], -1, MONTH)), MoM % = DIVIDE(SELECTEDMEASURE() - CALCULATE(SELECTEDMEASURE(), DATEADD(Dates[Date], -1, MONTH)), CALCULATE(SELECTEDMEASURE(), DATEADD(Dates[Date], -1, MONTH))), MTD = CALCULATE(SELECTEDMEASURE(), DATESMTD(Dates[Date])).
  2. Set a format string expression on MoM % to "0.0%" and on the others to SELECTEDMEASUREFORMATSTRING().
  3. Save to the model. Matrix: rows Category, columns Time Calc, values Net Sales, then switch value to Orders. Both get all four variants without new measures.
  4. Set precedence and observe what happens when you add a second calculation group "Currency" (below).
Expected result: Format strings switch correctly per column. Implicit measures stop working (turn on Discourage implicit measures).

Currency conversion calculation group

A · Guided 30 min · uses FactSales, ExchangeRates
  1. Relate ExchangeRates to Dates on RateDate (inactive is fine). Build a rate lookup measure: Rate = VAR c = SELECTEDVALUE(Currency[Code]) VAR dte = MAX(Dates[Date]) RETURN CALCULATE(MAX(ExchangeRates[RateToUSD]), ExchangeRates[Currency] = c, ExchangeRates[RateDate] <= dte, LASTDATE(...)) — or a simpler LASTNONBLANK pattern.
  2. Calculation group "Currency": USD = SELECTEDMEASURE(), Selected = SUMX(VALUES(Dates[Date]), CALCULATE(SELECTEDMEASURE()) / [Rate]) where Rate is the last known rate for that date.
  3. Currency slicer from a disconnected table. Check Net Sales in EUR for January.
Expected result: Converting per day then summing gives a different number than converting the monthly total by one rate; you can explain which is correct.

TREATAS and virtual relationships

A · Guided 30 min · uses FactBudget, FactSales
  1. Delete the DimCategory relationship to FactBudget from the Intermediate model.
  2. Budget via TREATAS = CALCULATE(SUM(FactBudget[BudgetAmount]), TREATAS(VALUES(DimProduct[Category]), FactBudget[Category]), TREATAS(VALUES(Dates[YearMonth]), FactBudget[YearMonth])).
  3. Compare against the physical relationship version in DAX Studio timings.
  4. Do the same with CROSSFILTER to temporarily change a relationship direction inside a measure.
Expected result: Same numbers; TREATAS is slower on large budgets but has no model impact.

Virtual tables and window functions

A · Guided 40 min · uses FactSales, DimCustomer
  1. Customer ABC Class = VAR t = ADDCOLUMNS(ALLSELECTED(DimCustomer[CustomerKey]), "@s", [Net Sales]) VAR total = SUMX(t, [@s]) VAR cur = [Net Sales] VAR cum = SUMX(FILTER(t, [@s] >= cur), [@s]) RETURN SWITCH(TRUE(), DIVIDE(cum,total) <= 0.7, "A", DIVIDE(cum,total) <= 0.9, "B", "C").
  2. Rewrite the running total with WINDOW: Running Sales = CALCULATE([Net Sales], WINDOW(1, ABS, 0, REL, ALLSELECTED(DimCustomer[CustomerName]), ORDERBY([Net Sales], DESC))).
  3. Use OFFSET to get the previous customer's sales in a sorted table, and RANK for the position.
  4. Explain in a comment why WINDOW cannot be wrapped in a non-trivial FILTER.
Expected result: A/B/C classification sums to 100% of customers; Running Sales in the last row equals the total.

Interview questions

  • Calculation group precedence: what does it decide?
  • LASTDATE vs LASTNONBLANK vs MAX for semi-additive measures.
  • TREATAS vs relationship: three differences.
  • What does SELECTEDMEASUREFORMATSTRING() solve?
  • When do window functions (WINDOW, OFFSET, INDEX, RANK) beat the classic EARLIER / FILTER patterns?
  • How do you handle a currency conversion where the rate is missing for a date?

Assessment

Summing FactInventory[QuantityOnHand] over 10 weeks for one product gives:

  • The current stock
  • 10× the average stock, meaningless
  • Zero
  • An error

With calculation groups present, implicit measures (dragging a column):

  • Work normally
  • Are ignored by calculation items and should be disabled
  • Become measures
  • Error

TREATAS(VALUES(DimProduct[Category]), FactBudget[Category]) fails silently if:

  • Category has more than 4 values
  • The text values differ in case or spacing
  • FactBudget has more rows
  • Dates is not marked

Build the Time Calc group and prove MTD works for Net Sales and for Orders without writing extra measures. Show the matrix with 8 columns.

If Orders MTD shows a $ sign, the format string expression is wrong.

Composite models, DirectQuery and hybrid tables

Data: FactSales DimProduct
Why it matters
Storage mode is an architecture decision: it fixes latency, cost and which DAX features you can use.
Typical production failure
A DirectQuery model "for real-time" hammers the ERP database at 9am and the operations team blocks the service account.
When to use it
Import by default; DirectQuery or hybrid tables when latency truly matters; Dual for shared dimensions; Direct Lake when data already lives in OneLake.
When not to
Choosing DirectQuery because "real time" was said in a meeting. Ask what latency the decision actually needs.

Assignments

DirectQuery reality check

A · Guided 40 min · uses FactSalesBig in SQL (any free SQL: SQL Express, Postgres, or Fabric warehouse)
  1. Load FactSalesBig to a SQL database. Connect with DirectQuery. Import the dimensions.
  2. Build the same summary page as Beginner. Open Performance Analyzer, copy a visual's DAX query and the generated SQL.
  3. Add a measure with DISTINCTCOUNT and one with a text-based calculated column. Observe which one makes the SQL explode or fails ("unsupported").
  4. Set "Assume referential integrity" on relationships and compare the SQL (INNER vs LEFT JOIN).
Expected result: You can show one generated SQL statement before and after Assume referential integrity.

Composite: extend a shared model

A · Guided 40 min · uses Your published shared model
  1. Desktop → connect live to the published Northwind model. Then "Make changes to this model" to turn it into a DirectQuery-to-semantic-model composite.
  2. Import a local Excel of sales targets and relate it to the remote DimRegion.
  3. Note the "limited relationship" icon. Write a measure that uses both. Publish.
  4. Delete a column in the source model and refresh the composite: read the error.
Expected result: A chain: your report → composite → shared model. You can explain what breaks upstream changes.

Hybrid table

A · Guided 30 min · uses FactSalesBig in SQL
  1. Incremental refresh policy on the DirectQuery table with "Get the latest data in real time with DirectQuery" on (needs Premium/Fabric capacity).
  2. Publish, refresh, inspect partitions: import partitions for history and one DirectQuery partition for today.
  3. Insert a row in SQL for today and confirm it appears without refresh.
Expected result: Today's data is live; history is imported.

Storage mode decision table

A · Guided 20 min · uses —
  1. Write a one-page decision table in a text box: for Import, DirectQuery, Dual, Direct Lake list max size, latency, DAX limitations, RLS behaviour, refresh needs.
  2. Set DimProduct to Dual and explain in a comment why dimensions in a composite should be Dual.
Expected result: You choose Dual for dimensions shared between an import agg and a DQ fact, and you can say why.

Interview questions

  • Why should dimensions be in Dual storage mode in a composite model?
  • What is a limited (weak) relationship?
  • DirectQuery over a shared semantic model: what is the security and refresh story?
  • Give three DAX functions that are not supported or are slow over DirectQuery to SQL.
  • When is Import the wrong choice even if the data fits?

Assessment

Assume referential integrity changes the generated SQL from:

  • UNION to JOIN
  • LEFT OUTER JOIN to INNER JOIN
  • INNER to LEFT
  • No change

A Dual-mode table:

  • Is always imported
  • Is always DirectQuery
  • Behaves as import or DQ depending on the query
  • Is a hybrid table

Hybrid tables require:

  • Pro workspace
  • Premium/Fabric capacity
  • A gateway in personal mode
  • Dataflows

Build a composite model over your shared Northwind model plus a local targets table, publish it, and list every feature that became unavailable versus a pure import model.

Calculated tables on remote, some format options, Q&A, certain visuals.

Deployment: pipelines, Git, PBIP, TMDL, XMLA, Tabular Editor

Data: FactSales
Why it matters
Without source control and promotion between environments, every change is made live in production and nobody can say what changed or roll it back.
Typical production failure
Two developers overwrite each other's published model; a measure silently changes and the board pack is wrong for a week.
When to use it
PBIP + Git for every shared model; deployment pipelines or Fabric Git integration to move Dev → Test → Prod.
When not to
Editing the production model directly through XMLA as a habit; it bypasses review and breaks .pbix download.

Assignments

PBIP and Git

A · Guided 40 min · uses Your report
  1. Save your report as a Power BI Project: File → Save as → .pbip. Current Desktop builds save the model as TMDL and the report in PBIR, the enhanced report format that is now the default. Older builds needed preview switches for both.
  2. Initialise a Git repo, commit. Open the folder: .SemanticModel/definition/*.tmdl and .Report/definition.
  3. Change a measure in Desktop, save, git diff. Then change the same measure directly in the .tmdl file while the project is open: Desktop detects the external change and offers to apply it.
  4. Connect the workspace to a Git branch (Fabric Git integration). Commit from Desktop, sync in the Service.
Expected result: A readable diff showing exactly one measure expression change.

Deployment pipeline with rules

A · Guided 40 min · uses Dev/Test/Prod workspaces
  1. Create a deployment pipeline with the three workspaces. Assign Dev.
  2. Deploy to Test. Add a deployment rule that changes the Environment parameter (from the Intermediate parameter assignment) to "Test".
  3. Compare Test vs Dev after changing a measure in Dev: the pipeline shows "Different". Deploy again.
  4. Deploy only the semantic model, not the report, using selective deployment.
Expected result: Prod points at the Prod source without anyone editing the PBIX.

Tabular Editor scripting

A · Guided 40 min · uses Your model
  1. Open the model in Tabular Editor 2. Write a C# script that creates, for every measure ending in "Sales", a sibling measure "{name} PY" using SAMEPERIODLASTYEAR.
  2. Run Best Practice Analyzer with the standard rules. Fix every violation, or document why not.
  3. Use the XMLA endpoint (needs PPU/capacity) to open the published model, rename a measure, save. Note that the model can no longer be downloaded as PBIX.
  4. Format all DAX with the built-in formatter.
Expected result: BPA shows 0 errors. You can list three BPA rules you disagree with and why.

Semantic model versioning and rollback

A · Guided 20 min · uses Your model
  1. Tag a Git commit as v1.0. Deploy. Make a breaking change (rename a column a report uses). Deploy.
  2. Roll back by checking out the tag and syncing to the workspace.
  3. Write a one-paragraph release note template you would send to report authors.
Expected result: The broken visual is fixed after the rollback without touching the report.

Interview questions

  • PBIX vs PBIP: why does it matter for a team?
  • What does a deployment rule change and what can it not change?
  • After editing a model via XMLA, why can't you download the PBIX anymore?
  • How do you deploy a model without triggering a full refresh?
  • What is the difference between Fabric Git integration and deployment pipelines, and do you use both?

Assessment

TMDL is:

  • A DAX dialect
  • A text serialisation of the tabular model definition
  • A refresh log
  • A visual container

Deployment rules apply to:

  • Reports
  • Semantic models and dataflows (parameters, sources)
  • Dashboards
  • Apps

Best Practice Analyzer rule "Avoid bi-directional relationships" fires. Best response:

  • Always remove them
  • Justify or remove each; bridge-table security filters are a valid exception
  • Ignore BPA
  • Convert to many-to-many

Save your model as PBIP, write a Tabular Editor script that adds a description to every measure lacking one, commit, and show the git diff line count.

foreach(var m in Model.AllMeasures) if(string.IsNullOrEmpty(m.Description)) m.Description = "TODO";

Dataflows, Fabric Lakehouse and Direct Lake

Data: FactSales FactInventory WebEvents
Why it matters
Storage mode decides how fresh the numbers are, how fast reports are and what the capacity costs. Choosing it wrongly is expensive to undo.
Typical production failure
A Direct Lake on SQL model built over SQL views silently runs every query as DirectQuery, and the dashboard that was fast in the demo takes 20 seconds in production.
When to use it
Direct Lake when the data already lands in OneLake as Delta tables and is too large or too fresh to copy with Import.
When not to
When the model author cannot change the upstream tables and needs Power Query transformations: use Import tables (possibly combined with Direct Lake on OneLake).

Assignments

Dataflow Gen2 as a shared staging layer

A · Guided 40 min · uses RawOrdersExport
  1. Create a Dataflow Gen2 in a Fabric workspace. Paste the RawOrdersExport clean-up M from Beginner.
  2. Set the output destination to a Lakehouse table "orders_clean".
  3. Point a new semantic model at the Lakehouse (SQL endpoint) and another at the Dataflow directly. Compare refresh behaviour.
  4. Break the source (rename a column) and read the Dataflow error versus the model error.
Expected result: One transformation, two consumers, one place to fix it.

Lakehouse and Direct Lake

A · Guided 40 min · uses All datasets as CSV
  1. Upload all fourteen CSVs to a Lakehouse Files area. Load each to a Delta table.
  2. Create a Direct Lake semantic model from the Lakehouse and note which option you created: Direct Lake on OneLake (the recommended option for new models) or Direct Lake on SQL analytics endpoint. Build relationships in the web modeling view.
  3. In DAX query view run EVALUATE TABLETRAITS() and read [DirectLakeFallbackInfo] for each table. None means the table is served by Direct Lake.
  4. In the SQL analytics endpoint, create a view over FactSales and add it to a Direct Lake on SQL model. Run TABLETRAITS again: the view falls back to DirectQuery. Set the model's Direct Lake behavior to DirectLakeOnly and watch the same visual fail instead of falling back.
  5. Try the same on a Direct Lake on OneLake model: it will not accept a non-materialized view at all. Materialize the logic as a Delta table instead.
Expected result: You can say what forces DirectQuery fallback in Direct Lake on SQL (SQL views, SQL-endpoint RLS, OLS or masking, guardrails exceeded, tables not yet framed) and why Direct Lake on OneLake never falls back: it errors instead, so you design to stay inside its limits.

Notebook to Power BI

A · Guided 40 min · uses WebEvents
  1. In a Fabric notebook (PySpark or Python), load WebEvents, compute per-session first and last event and a funnel stage, write a Delta table "sessions".
  2. Refresh the Direct Lake model (reframe) and build a funnel visual.
  3. Schedule the notebook and the reframe in a pipeline.
Expected result: Funnel shows PageView → Purchase drop-off from the notebook output, not from DAX.

Choose the right layer

A · Guided 20 min · uses —
  1. For each of these, write where the logic lives and why: currency conversion, customer ABC class, data cleaning of the messy export, budget allocation to days, RLS mapping, session duration.
  2. Rule you are testing: transform as far upstream as possible, as far downstream as necessary.
Expected result: At most two of the six land in DAX.

Interview questions

  • Direct Lake vs Import vs DirectQuery.
  • What is "reframing" in Direct Lake?
  • When does a Dataflow beat Power Query inside the model?
  • Where should a business rule like "returns are excluded from revenue" live?
  • What is the SQL analytics endpoint and what is it not for?

Assessment

A Direct Lake on SQL analytics endpoint model falls back to DirectQuery when:

  • The model has more than 4 tables
  • A table is based on a non-materialized SQL view, or the SQL endpoint applies RLS/OLS
  • It is refreshed (framed)
  • The semantic model itself has RLS roles

Dataflow Gen2 output destination options include:

  • Only the dataflow storage
  • Lakehouse, Warehouse, KQL DB, Azure SQL among others
  • Only OneDrive
  • Only Excel

Reframing a Direct Lake model:

  • Copies all data again
  • Re-reads Delta metadata only
  • Requires a gateway
  • Deletes the cache permanently

Build a Direct Lake model over the 14 CSVs and reproduce the Beginner executive page. Prove one visual is served by Direct Lake, not fallback.

Run EVALUATE TABLETRAITS() and check [DirectLakeFallbackInfo] = None for every table, or look for DirectQuery events in a DAX Studio trace.

Governance, enterprise architecture and the "senior" conversations

Data: FactSales DimEmployee
Why it matters
Governance is what lets a company trust a number without asking who built it.
Typical production failure
Three "Revenue" reports disagree in a board meeting and nobody knows which model is authoritative.
When to use it
Certification for models that many reports depend on, sensitivity labels for anything confidential, usage and lineage to decide what to retire.
When not to
Governance as a gate that blocks every change. Make the certified path the easiest path.

Assignments

Object-level security

A · Guided 20 min · uses DimEmployee
  1. In Tabular Editor, create a role "NoSalary" and set OLS on DimEmployee[Salary] to None.
  2. Publish. Add a test user. Open the report: the visual using Salary must show an error, others must work.
  3. Combine with the RLS role and confirm both apply.
Expected result: Only the salary visual breaks for the restricted user; you can explain why OLS breaks the visual instead of hiding the column.

Certified model with documentation

A · Guided 30 min · uses Your shared model
  1. Add descriptions to every visible measure and table (Tabular Editor script from the Deployment topic).
  2. Add synonyms for Q&A on the top five fields.
  3. Request certification from your admin (or certify as admin in a trial tenant). Add a sensitivity label.
  4. Create a "Model guide" report page listing every measure, its definition and owner, generated from INFO.MEASURES() in a DAX query.
Expected result: Analysts can find and understand the model without asking you.

Usage and lineage

A · Guided 20 min · uses Your workspace
  1. Open the usage metrics report for your report. Find the least-viewed page.
  2. Open lineage view for the workspace. Identify everything that breaks if the Lakehouse is deleted.
  3. Use the Admin monitoring workspace (or the Fabric Capacity Metrics app) to find the model consuming the most CU.
Expected result: A list of three items you would retire and one you would optimise.

The senior conversation

A · Guided 30 min · uses —
  1. Write, in under 200 words each, your answer to: (1) "Why is the report slow?" for a CFO, (2) the same for a data engineer. (3) "Should we go Fabric?" for a CTO. (4) "Why can't I just export everything to Excel?" for an analyst.
  2. Read them aloud. Cut any sentence a listener would not understand.
Expected result: Four answers, none containing the word VertiPaq for the CFO, all containing a recommendation.

Interview questions

  • Design a semantic-model architecture for 50 reports across 6 departments.
  • OLS vs RLS vs hiding a column.
  • What are the key tenant settings you review on day one at a new company?
  • How do you handle a request for a report you know is wrong (e.g. summing a snapshot)?
  • What is your checklist before calling a model production-ready?
  • How do you estimate capacity needs?

Assessment

A user in an OLS role opens a visual using a restricted column. Result:

  • Column hidden, visual works
  • Visual shows an error
  • Blank values
  • The role is ignored

Certification of a semantic model can be granted by:

  • Any Pro user
  • Users/groups allowed by the tenant setting
  • Workspace Viewers
  • Only Microsoft

Lineage view helps you answer:

  • Which measure is slow
  • Which downstream items depend on a source
  • Who viewed a report
  • How much a capacity costs

Produce the "Model guide" page using INFO.MEASURES() and INFO.TABLES() in a DAX query, exported to a table in a report, and include the description column.

EVALUATE SELECTCOLUMNS(INFO.MEASURES(), "Measure", [Name], "Expression", [Expression], "Description", [Description]).