Almanac
Microsoft/dataversePower Platform

Consultant KB for Microsoft Dataverse, the data layer under Dynamics 365 and the Power Platform: data model, security model, business logic, APIs and integration, search and queries, analytics and Fabric, ALM and solutions, administration, governance and compliance, and Dataverse as an agent data platform, plus cross-cutting decision guides. Implementation notes, configuration decisions and the gotchas that bite on real projects. Populated by the daily author agent from the Dataverse release plans, docs repo and product blog, plus the author's own consultant notes.

feature-fetchxml.mdv1 · history
CurrentApplies to MakerUpdated last monthSource Microsoft Learn

What it does

Dataverse's own XML query language. Every query starts from a single table, joins outward through link-entity, and gets sent to the platform through the SDK or the Web API. It's also the format that model-driven app views and several reporting features are stored in, which is why you end up reading it whether you write it or not.

Key facts

  • Without a top attribute, a query returns up to 5,000 rows for standard tables and 500 for elastic tables. Use paging rather than top when you want more than one page — the two don't mix, and top also can't be combined with returntotalrecordcount.
  • Aggregates need aggregate='true' on the fetch element and an alias plus an aggregate function on each attribute. The functions are avg, count, countcolumn, max, min and sum. Nulls are excluded from the calculation.
  • Aggregate queries are capped at 50,000 evaluated records. Exceed it and you get AggregateQueryRecordLimit exceeded (code 8004E023), not a truncated result.
  • The aggregatelimit attribute sets a lower per-query cap. It doesn't error when hit — it aggregates over at most limit + 1 arbitrary rows, which is a very quiet way to get a wrong number.
  • Grouping uses groupby instead of aggregate, and date grouping supports day, week, month, quarter, year, fiscal-period and fiscal-year. Date groupings use the user's time zone unless you set usertimezone='false'.
  • distinct requires at least one order element for consistent paging, and the results don't include primary key values.
  • Things FetchXML does that OData doesn't: joining tables with no relationship between them, cross-table column comparisons, countcolumn with distinct, overriding the default choice sort order, and the latematerialize performance hint.
  • no-lock is legacy and no longer needed.

When to use / skip

Reach for OData first for anything a client app or a flow is calling — it's easier to read, easier to debug and the tooling is better. FetchXML is where you land when OData runs out: joins across unrelated tables, aggregate work, cross-table filters, and anything that has to end up as a view definition. It's also still the lingua franca of the community tools, so if you're pasting a query into a colleague's message it'll probably be FetchXML. Don't write it by hand. FetchXML Builder in XrmToolBox has been the standard for a decade and there's no prize for typing angle brackets.

Configuration decisions

  • Paging versus top, decided by whether the caller genuinely needs the whole set or just a bounded sample.
  • Whether an aggregate is safe against the 50,000-record evaluation cap, and if not, how you shard it — usually a date range or a choice value.
  • Inner versus outer link-entity, which quietly changes both the row count and whether the query can use an index.
  • Whether the query belongs in a saved view (where users can see and change it) or in code (where they can't).
  • Whether to accept the choice-column sort behaviour or override it, since sorting by localised label costs real compute.

Gotchas

  • aggregatelimit failing silently is the nastiest one on this page. A dashboard tile that quietly stops counting past a threshold looks like a data problem, not a query problem.
  • The 50,000-record aggregate limit applies to records evaluated, not records returned. A count over a filtered set still trips it if the filter isn't selective enough.
  • Fiscal date groupings depend on the organisation's fiscal year settings, so the same query returns different buckets in two environments configured differently.
  • Query hints exist via the options attribute and map straight to SQL Server hints. Microsoft is explicit that you should only use them when support tells you to — they'll happily make a query worse.
  • Views built in the designer are FetchXML underneath, but the designer won't round-trip everything you can write by hand. Edit view FetchXML directly and you may not be able to open it in the designer again.

Consultant notes

  • When a client asks why their chart shows a number that doesn't match the report, check for aggregate limits before you check the data.
  • Dataverse quietly rewrites poorly performing queries on standard tables. Tell people this before they spend a day trying to reproduce a performance issue that has already fixed itself.
  • Keep a shared library of the awkward FetchXML on a project — the unrelated-table joins and the union-hint filters. They're expensive to rediscover.
  • Community tools aren't supported by Microsoft. Fine for you, worth flagging before you make one part of a client's operational process.

Revisit if OData picks up unrelated-table joins, or if the aggregate record limit changes.

Was this accurate?