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
topattribute, a query returns up to 5,000 rows for standard tables and 500 for elastic tables. Use paging rather thantopwhen you want more than one page — the two don't mix, andtopalso can't be combined withreturntotalrecordcount. - Aggregates need
aggregate='true'on the fetch element and analiasplus anaggregatefunction on each attribute. The functions areavg,count,countcolumn,max,minandsum. 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
aggregatelimitattribute 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
groupbyinstead ofaggregate, and date grouping supports day, week, month, quarter, year, fiscal-period and fiscal-year. Date groupings use the user's time zone unless you setusertimezone='false'. distinctrequires at least oneorderelement 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,
countcolumnwith distinct, overriding the default choice sort order, and thelatematerializeperformance hint. no-lockis 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
aggregatelimitfailing 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
countover 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
optionsattribute 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.