Simple Object Designer Documentation
Index

Create Queries

This page explains how to build data queries using Simple Object Designer.

Accessing Query Creation

  1. Open Expose Data > Data Queries
  2. Click New to create a query
  3. The Query Card opens

Query Card Settings

Basic Information

Field Description Example
Query Name Internal identifier (letters and numbers only, max 27 characters) "CustomerSalesSummary"
Caption Display name "Customer Sales Summary"
Query Category Grouping "Sales"
Available as API Expose the query as an OData API endpoint
Usage Category Where the query appears in Business Central (None, Lists, Tasks, ReportsAndAnalysis, Documents, History, Administration)

API Settings

The API Settings group is visible when Available as API is enabled:

Field Description
Entity Name Singular API identifier
Entity Caption Display name for the entity
Entity Set Name Plural collection name
Entity Set Caption Display name for the collection
Group API group used in the endpoint URL (defaults to the Object Prefix from setup)

When you enter an Entity Name, the caption and set names are suggested automatically. Entity Name and Entity Set Name must differ — the set name should be the plural form (e.g. customer vs customers).

Building Query Structure

Step 1: Add Root Data Item

  1. In Query Elements, add first data item
  2. Select the primary table
  3. This is the root of your query

Step 2: Add Columns

For each data item:

  1. Select the element and click the Columns action
  2. Tick Included in Query for the fields you want in the output
  3. Choose a Method (aggregation or date part) if needed

To join additional tables:

  1. Add new data item (new elements are appended at the end, as a child of the element above)
  2. Select the table
  3. Configure link to parent
  4. Set an appropriate SQL Join Type — LeftOuterJoin or FullOuterJoin, for example

Current limitation: one element per level. Queries currently support only one element at each level of the hierarchy. Attempting to add a data item as a sibling of an existing element fails with:

New elements must be added at the end of the query.

New elements can only be appended at the end, as a child of the element above them. The error appears regardless of which row position you try to insert at, and it does not mean you have hit a maximum table count.

There is no limit on the total number of tables — nine tables in a chain is fine. The constraint is purely that two tables cannot sit at the same level, which means several tables that all need to link back to the root by the same key cannot be added as direct siblings.

In practice, queries with more than two data items are currently unreliable and may produce this error even when the element is genuinely at the end. Improved multi-level support is actively being worked on.

Links connect a data item to its parent:

  1. On the child element, choose the SQL Join Type (defaults are fine for most cases; use LeftOuterJoin or InnerJoin)
  2. Click the Filters action on the child element
  3. Add a line with Filter Type Link: pick the field on the child element and the corresponding field on the parent table

Step 5: Arrange the Hierarchy (optional)

Queries support multiple levels. Multiple siblings under the same parent are not yet supported — see the limitation under Step 3. Use the actions on the Elements part to restructure:

Action Effect
Move Up / Move Down Reorder an element (and its children) among its siblings
Indent Make the element a child of the element above it at the same level
Outdent Move the element one level up, next to its current parent

Rules:

  • A query has exactly one top-level (root) element
  • Every child element needs at least one filter of type Link (or SQL Join Type CrossJoin)
  • After moving an element under a different parent, review its Link filters - they reference the previous parent table

Query Elements

The Elements Part

Shows data item hierarchy:

  • Root table at top
  • Child tables indented under their parent
  • Columns under each table

Note: multiple children sharing the same parent is not currently supported — only one element per level. This is planned for a future release.

Element Types

Type Description
DataItem A table in the query
Column A field to include

Element Properties

Property Description
Name Element identifier
Caption Display name
Data Type For columns

Configuring Columns

The Columns Page

Select an element and click Columns. The Query Element Columns page lists every field on the element's table:

Column Description
Field Number / Field Name The field on the table
Class Field class — FlowFields can slow the query down, so only include the ones you need
Type The field type
Included in Query Tick to include the field as an output column
Filter field in Query Tick to make the field available as a filter for API consumers (this also includes the field)
Method Aggregation or date-part method for the column (see below)

Output column names are derived from the field names (lower-cased, with spaces and special characters removed).

Field types that cannot be query columns

  • FlowFields are not supported in queries. This is a Business Central platform limitation, not an SOD one. The Columns page does list them (Class = FlowField), but including one can make the query — and any API exposing it — fail at consumption time. If you need FlowFields, expose the data through a regular table API endpoint instead, where they are supported and calculated automatically. See Create API Endpoints.
  • BLOB fields cannot be included. Doing so makes the query fail at runtime with Nav.Types.Exceptions.NavNotSupportedException ("NotSupported"). More generally, a NotSupported exception when running a query points at a field type that is not query-compatible — remove it and republish.

Calculated and computed columns are not possible

Calculated fields cannot be added to a query; Business Central does not support them in query objects. That includes:

  • Concatenating fields to build a value (an item number plus a variant suffix, for example)
  • Supplying a default value when a field is null
  • Anything resembling a SQL CASE expression

The only per-column transformations available are the documented Methods below plus the Add Count Column flag. Nothing else. This is not planned to change — queries are limited to what Microsoft provides in the platform.

Per-Column Methods

The Method column on a query column can transform that column's value:

Method Description Use For
(blank) Raw value Regular columns
Day Day component of a date Date breakdowns
Month Month component of a date Date breakdowns
Year Year component of a date Date breakdowns
Sum Total Amounts, quantities
Average Mean value Averages
Min Minimum Lowest value
Max Maximum Highest value

Day/Month/Year can only be used on Date or DateTime fields; Sum/Average/Min/Max only on numeric fields.

To get a row count, do not look for a Count method on a column — there isn't one. Enable the Add Count Column flag on the data item instead, which adds a count column to the result.

Links are defined on the element's Filters page, as filter lines with Filter Type Link:

Property Description
Filter Field Field on this (child) element
Field Value Field No. Corresponding field on the parent table

Add multiple Link lines to join on more than one field. The parent field is automatically added to the parent element's included columns.

Support for more complex queries, including multiple Link lines, arrived in a recent SOD update. If adding a second Link produces an error, update SOD to the latest version.

Joining tables that share a field name

When the linking field has the same name in both tables — ServiceCode as the primary key in one table and a foreign key in the other, for instance — include only one of the two identically named columns in the query output. The other exists purely to establish the link.

Including both makes publishing fail with A member of type QueryColumn with name '<fieldname>' is already defined in Query '<QueryName>'. If the columns are genuinely correct and the error persists, refresh symbols — a stale cache can produce the same message. See Publishing Failures.

Join Types

Type Description
LeftOuterJoin All parent records; missing children come back as blanks
InnerJoin Only records that match on both sides
RightOuterJoin All child records; missing parents come back as blanks
FullOuterJoin Union of left + right outer
CrossJoin Cartesian product (no link required)
Parent: Customer
Child: Sales Invoice Header
Link: Customer.No. = Sales Invoice Header.Sell-to Customer No.

Adding Filters

Filters restrict which records an element includes. They are added on the same Filters page as links, using one of two filter types:

Filter Type Description Filter Value Example
Const The field must equal a fixed value true, Posted
Filter The field must match a filter expression >=01012024, 1000..2000, A*

Steps:

  1. Select the element and click Filters
  2. Add a line: pick the field, choose Const or Filter, and enter the value
  3. The value is validated as you enter it (for example, a Const on a Boolean field must be true or false, and Option/Enum values must match an existing member)

The filter is baked into the query and applies every time the query runs. API consumers can apply additional filters at request time.

Validating and Testing Queries

Before Publishing

  1. Review query structure
  2. Verify links are correct
  3. Check column selection

Query definitions are validated when you publish. The most common publish errors are:

  • Query has no root element, cannot publish. — every query needs exactly one top-level data item. Add the root data item (Query Elements > first data item > primary table), or delete the incomplete query.
  • The query has no columns
  • A child element has neither a Link filter nor SQL Join Type CrossJoin
  • A CrossJoin element also has Link filters (remove one or the other)
  • Two identically named columns from a join (see Joining tables that share a field name)

An incomplete query blocks the entire publish — including unrelated API endpoints and features. When a publish error appears while you are working on a query, review the query definition for completeness before assuming a platform or symbol problem. Refreshing symbols and updating SOD will not fix a configuration gap.

This bites most often with queries created by accident: autosave can persist a query you started and never populated, and testing the Data Query function can leave an empty Query category behind. Delete anything you are not using.

Permission-set compile errors. A query that references custom feature tables can also fail at compilation with AL0185 (query missing from the permission set) and PTE0004 on the referenced tables. See Publishing Failures — including the option to send the .app file to support, renamed to .txt if mail filtering blocks it.

After Publishing

  1. Access via API, or open the query in Business Central (if a Usage Category is set, the query can be found via Tell Me)
  2. Verify data returns correctly
  3. Check joins work properly

Common Query Patterns

Master-Detail

Customer with orders:

Customer
└── Sales Header
    ├── Customer No.
    ├── Order Date
    └── Amount

Summary with Aggregation

Sales by customer:

Customer
├── No.
├── Name
└── Sales Header (Sum Amount)

Multi-Level Join

Order with lines and items:

Sales Header
├── Header fields
└── Sales Line
    ├── Line fields
    └── Item
        └── Item fields

Source Table Limitations

Most tables in the 2000000xxx range — virtual and system tables — cannot be used as the source of a data query. They are not real tables in the database; the platform populates them on the fly, and they do not support the same operations as normal tables. Table 2000000138 (Table Information) is a concrete example.

Symptom when attempted: publishing crashes.

Workaround: create your own normal table, populate the fields you need into it (through a codeunit or a scheduled job), and point the data query at that table instead.

Working Out the Query Logic

SOD cannot show or export the SQL a query generates, and this is unlikely to change.

The reason is structural: the SQL string is not produced by SOD or by AL at all. Business Central query objects are compiled by the platform, and the actual SQL is generated by the runtime at execution time, taking security filters, company and table extensions into account. There is no design-time step where a ready-to-paste SQL string exists.

A practical workflow for iterating quickly: build and validate the join, filter and column logic first in a SQL tool such as Interactive SQL, and translate the result into the query card once you are satisfied. This avoids a publish cycle for every experiment. Business Central query joins map fairly directly onto SQL joins, so a query proven in SQL usually translates cleanly.

Best Practices

Query Design

  • Start with clear objective
  • Keep joins minimal — and remember only one element per level is supported today
  • Include only needed columns
  • Avoid FlowFields and BLOB fields entirely
  • Consider performance

Naming

  • Use descriptive names
  • Follow conventions
  • Document purpose

Testing

  • Test with various data
  • Verify aggregations
  • Check filter behavior

Housekeeping

  • Delete queries you started and did not finish — an empty or root-less query blocks every publish