Technical6 min read

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

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).