X++ Query and QueryBuildDataSource: dynamic query examples
Build a dynamic query in X++: Query, QueryBuildDataSource, QueryBuildRange, links and QueryRun. Microsoft Learn examples and common mistakes.
The short answer
A dynamic X++ query is built with the Query object model: a Query object, QueryBuildDataSource data sources (a child data source of another creates a join), QueryBuildRange ranges (translated into a WHERE clause), QueryBuildLink links between data sources, then QueryRun to run the query and loop over results.
You use it when the query must be user-editable (batch or report dialog), reused by a framework (change tracking, data provider) or built from parameters. For a simple hard-coded read, an X++ select stays more readable. Microsoft also offers the SysDa API for extensible queries without the QueryRun overhead.
The Query model classes
- Query: the top of the definition, with one or more data sources.
- QueryBuildDataSource: access to one data source. Two sources at the same level produce separate SQL statements; a child source creates a join.
- QueryBuildFieldList: the returned fields (all by default), with possible aggregates (SUM, COUNT, AVG).
- QueryBuildRange: a restriction on one field, translated into WHERE.
- QueryBuildLink: the relation between two joined sources (on the child source).
- QueryBuildDynalink: a link to an external record, used notably by synchronised forms.
- QueryRun: runs the query and fetches data.
Example: joins, range and links
Microsoft Learn example (change tracking query): customers, parties, primary address.
public static Query defaultCTQuery()
{
Query q = new Query();
QueryBuildDataSource custDs = q.addDataSource(tableNum(CustTable));
QueryBuildDataSource partyDs = custDs.addDataSource(tableNum(DirPartyTable));
partyDs.relations(true);
QueryBuildDataSource locationDs = partyDs.addDataSource(tableNum(DirPartyLocation));
locationDs.addRange(fieldNum(DirPartyLocation, IsPrimary)).value(queryValue(NoYes::Yes));
locationDs.addLink(fieldNum(DirPartyTable, RecId), fieldNum(DirPartyLocation, Party));
return q;
}relations(true) uses the relations defined on the tables; addLink states the join fields explicitly; queryValue turns a value into a range string. To loop over the results:
QueryRun queryRun = new QueryRun(query);
while (queryRun.next())
{
CustTable custTable = queryRun.get(tableNum(CustTable));
// ...
}Common mistakes
- Adding two sources at the same level expecting a join: they are two separate queries.
- Building a range by string concatenation without queryValue: risk of errors with special characters.
- In a form, adding a range after super() in executeQuery: BP rule BPCheckinvalidExecuteQuery.
- Using QueryRun for a simple, fixed read: a select is clearer, or SysDa if extensibility matters.
- Forgetting relations(true) or addLink: a cartesian join.
Having it generated
Dynamic queries for batch jobs, reports or data providers: repetitive code that AI generates well when given the context. Skalp AI writes the specification, generates the X++ code and unit tests, compiles with a best-practice check, then delivers a pull request to your Azure DevOps: nothing is merged without your technical architect's approval. See AI X++ development, the bigger picture in building X++ without a consultant in 2026, and the Basic, Pro and Max plans (1 credit, ≈ €400 on Basic, €200 on Pro, consumed only when the code is generated).
See also: computed columns in a data entity.
Sources
- Microsoft Learn, "X++ class library", updated 31 Mar 2026, accessed 18 Oct 2026.
- Microsoft Learn, "Enable SQL change tracking for data entities", updated 26 Sep 2026, accessed 18 Oct 2026.
- Microsoft Learn, "Access data by using the SysDa classes", updated 31 Mar 2026, accessed 18 Oct 2026.
Frequently asked questions
How do you join with QueryBuildDataSource?
Add the second source as a child of the first (parentDs.addDataSource(...)), then relations(true) or addLink to specify the fields.
Query or select in X++?
Select for a fixed, readable read; Query when the query must be user-editable or built dynamically. SysDa for extensible queries with no overhead.
What is queryValue for?
To convert a value (enum, date, text) into a correctly formatted string for a query range.
Ready to transform your D365?
Join the CIOs who develop their D365 tickets in 2–4 h for 1 credit (≈ €400 on Basic, €200 on Pro).