Supported queries
| Frontend | Features |
|---|---|
SDK / SOAP (ServiceClient) |
QueryExpression, QueryByAttribute (criteria, nested filters, link-entity, order, TopCount, PageInfo) |
| FetchXML | FetchExpression via SDK and Web API ?fetchXml= (filters, link-entity, order, aggregates, top, page/count/paging-cookie) |
ExecuteCosmosSqlQuery (elastic tables) |
SDK message and Web API function: a subset of Cosmos-style SQL with c.props paths into JSON columns, paged by PagingCookie: see Querying JSON columns |
| OData (Web API) | $select, $filter, $orderby, $top, $count, $expand (single-valued), $apply, odata.maxpagesize + @odata.nextLink |
Supported operators (others return 400): eq, ne, gt, ge, lt, le, like, not-like, in, not-in, null, not-null,
begins-with, not-begin-with, ends-with, not-end-with, between, not-between, on, on-or-after,
on-or-before, last-x-days, next-x-days, last-x-months, this-month, this-year, eq-userid, ne-userid,
eq-businessid, eq-userteams, eq-useroruserteams (FetchXML and QueryExpression; not the OData query functions
Microsoft.Dynamics.CRM.*), under, eq-or-under, above, eq-or-above, not-under (on businessunit and on every table with a
hierarchical relationship: see Hierarchical operators; 400 on other tables),
eq-useroruserhierarchy, eq-useroruserhierarchyandteams (see Hierarchy security).
Aggregates: count, countcolumn (also distinct), sum, avg, min, max, groupby, dategrouping
(day, week, month, quarter, year).
Limits
- At most 5000 rows per page (
count,top,$top,odata.maxpagesizeabove 5000 return 400). - At most 50,000 records per aggregate query; beyond that: error
0x8004E023(AggregateQueryRecordLimitExceeded, HTTP 400). - Maximum nesting: 32 levels of filters, 10 levels of link-entity; at most 2000 SQL parameters per query
(an
incondition with more than 100 values uses a single parameter). Beyond that: 400InvalidArgument.
Paging
- Keyset on the primary key (with paging-cookie) when the ordering is only on the PK and the links are N:1.
- Otherwise
OFFSET/FETCHby page number.
Activities, party lists and note bodies
activitypointeris an ordinary table for queries: every activity has one row there, so one query finds the activities of every type regarding a record (regardingobjectideq the record's id; on the Web API$filter=_regardingobjectid_value eq <id>), andactivitytypecodefilters by type with a logical name (activitytypecode eq 'email', FetchXMLvalue="email") or a type code. Paging, sorting and aggregates work as on any table. The formatted value of itsstatuscodecomes from the option set of each row's activity table. See Activities and notes.- Party lists (
to,from,requiredattendees, …) can be selected on an activity (SDK, every page ofRetrieveMultiple; on the Web API through$expand={activity}_activity_parties, with$selectonly inside it), but not used in a condition, an order, a link'sfrom/to, an aggregate orgroupby, an alias, a link-entity's columns or a$select(400 naming the column). To filter by participant, link toactivitypartyonactivityidand filter itspartyidandparticipationtypemask. A distinct query that asks for a party list must also select the primary key (400 otherwise). An empty party list is left out of the record. annotation.documentbodyis read for the notes a page returns (also through a link-entity, named or with all attributes) but cannot be filtered, sorted, aliased, joined on or aggregated (400). A page whose bodies add up to more thanFiles:MaxNoteBodyBytesPerRead(64 MB by default) is a 400.$expandof the per-target navigations of the activity lookups (regardingobjectid_account_email,regardingobjectid_accountonactivitypointers,activityid_email,partyid_contact) and offileattachment'sobjectid_{table}is supported, one level deep.
Hierarchical operators
under, eq-or-under, above, eq-or-above and not-under (SDK ConditionOperator.Under, UnderOrEqual, Above,
AboveOrEqual, NotUnder) walk a table's hierarchical relationship (IsHierarchical: account_parent_account,
user_parent_user, a custom self-referencing relationship imported with a solution as hierarchical). One relationship per table can be
hierarchical.
- The condition's attribute is the table's primary key, or a lookup to a table that has a hierarchical relationship (for
example
parentaccountid, orowningbusinessunit); the value is the id of the referenced record. A table without a hierarchical relationship is a 400. - On
ownerid(targetssystemuserandteam) the operators walk the user hierarchy (user_parent_user), the one that applies to owners: a team id matches nothing (not-under: every row). Any other lookup with several targets (a Customer lookup such ascontact.parentcustomerid, or another polymorphic lookup) is refused with 400 ("not supported on the multi-target lookup") rather than walking one of its targets; filter on the target's primary key through a link-entity instead. The checklist compares this with Dataverse (hierarchy security, item 1 (u)). underreturns the descendants without the record itself,eq-or-underwith it;aboveandeq-or-abovewalk up.not-underexcludes the record and everything below it. An id that does not exist matches nothing (not-under: every row).- The walk is a recursive query limited to 100 levels (never an error) and stops on a cycle in the data. It ignores read
security, as
CountChildrendoes in Dataverse; the read filter still applies to the rows returned. eq-useroruserhierarchymatches a lookup or uniqueidentifier column that equals the caller or one of the caller's reports, down to the configured depth;eq-useroruserhierarchyandteamsadds the teams of the caller and of those reports. The business unit rule of hierarchy security does not apply. With the hierarchy model switched off they fail with 400.rowaggregate="CountChildren"in FetchXML is refused with 400 ("not supported") instead of being ignored.- The OData query functions (
Microsoft.Dynamics.CRM.Underand the others) are not supported in$filter, like every query function: they fail with 400 ("Dataverse query functions are not supported in $filter"); use FetchXML (?fetchXml=). Support for the query functions is a backlog item (#50).
Counting rows
$count=true and FetchXML returntotalrecordcount="true" give the same result: @odata.count with the number of
matching records, up to 5,000 for standard tables and 500 for elastic tables (Learn: "Count rows by using OData"). The SDK
gets the same value in EntityCollection.TotalRecordCount, and TotalRecordCountLimitExceeded is true when more records
match. When Prefer: odata.include-annotations names Microsoft.Dynamics.CRM.totalrecordcount,
Microsoft.Dynamics.CRM.totalrecordcountlimitexceeded, Microsoft.Dynamics.CRM.* or *, the response also carries
@Microsoft.Dynamics.CRM.totalrecordcount (-1 when no count was asked for) and
@Microsoft.Dynamics.CRM.totalrecordcountlimitexceeded. An aggregate query cannot ask for a count.
Known deviations
- Dates are evaluated in UTC (also
dategrouping). - Query functions (
Microsoft.Dynamics.CRM.*) are not supported in$filter, the hierarchical ones included. - Money values are formatted with the symbol and precision of the record's currency (
{money}_basewith the base currency's), in en-US digit grouping, ignoring per-user number formats; distinct queries format money in a record currency without symbol (N2) while{money}_basestill gets the base symbol, so one distinct result can mix formats; negative amounts are always-<symbol><amount>; and aggregate results have no formatted value. See Currencies. $skipis not supported (as in Dataverse).toptakes precedence over paging.- Ordering by lookup sorts by id, not by name.
- Global option set labels are present only if embedded in the attribute.
- Multi-valued
$expandand polymorphic$expandare not supported, except{activity}_activity_partiesand the per-target navigations of the activity and note lookups (see above); Customer and Owner lookups cannot be expanded. - A party list cannot be a column of a link-entity, and an empty party list is left out of the record (whether Dataverse returns an empty collection is in the activities checklist).
dategrouping="week"usesDATEPART(week): the week number depends on the SQL ServerDATEFIRSTsetting.- Nesting limits (filters 32, link-entity 10) and total parameter limit (2000), not present in Dataverse.
- A
distinctquery includes the primary key only if requested (explicit attribute or all columns); otherwise the returned records have no id and there is no paging-cookie. - The conditions of a link-entity can refer only to itself or to link-entities declared earlier (otherwise 400).
ConditionExpression.CompareColumnsandColumnSet.AttributeExpressionsare not supported (400).
Elastic tables
Queries on an elastic table accept a partitionId parameter, default to a page size of 500 and refuse link entities (0x80048d0b), $expand and a few sort and distinct combinations; partitionId on a standard table is a 400.
On an elastic table, ExecuteCosmosSqlQuery adds filters and projections over the paths inside JSON columns. It accepts SELECT ... FROM c WHERE ... ORDER BY with comparisons, IS_DEFINED, NOT, AND and OR, and refuses every other construct with a 400 that names it; see Querying JSON columns.