diff --git a/content/en/docs/apidocs-mxsdk/mxsdk/sdk-howtos/creating-the-domain-model.md b/content/en/docs/apidocs-mxsdk/mxsdk/sdk-howtos/creating-the-domain-model.md index e321e193f2d..2cebf8dee06 100644 --- a/content/en/docs/apidocs-mxsdk/mxsdk/sdk-howtos/creating-the-domain-model.md +++ b/content/en/docs/apidocs-mxsdk/mxsdk/sdk-howtos/creating-the-domain-model.md @@ -68,7 +68,7 @@ invoice.location = { x: 400, y: 100 }; *Studio Pro Guide* -* [Domain Model](/refguide/domain-model/) +* [Data in the Domain Model](/refguide/domain-model/) * [Entities](/refguide/entities/) Metamodel reference guide diff --git a/content/en/docs/appstore/use-content/platform-supported-content/modules/aws/aws-authentication.md b/content/en/docs/appstore/use-content/platform-supported-content/modules/aws/aws-authentication.md index 221c45e52e7..ffba3056abe 100644 --- a/content/en/docs/appstore/use-content/platform-supported-content/modules/aws/aws-authentication.md +++ b/content/en/docs/appstore/use-content/platform-supported-content/modules/aws/aws-authentication.md @@ -218,7 +218,7 @@ To help you work with signature version 4 headers, the following sections of thi #### Domain Model {#domain-model} -The domain model is a data model that describes the information in your application domain in an abstract way. For more information, see [Domain Model](/refguide/domain-model/). +The domain model is a data model that describes the information in your application domain in an abstract way. For more information, see [Data in the Domain Model](/refguide/domain-model/). You can view the domain model in the **App Explorer** in the **AWS Authentication** > **Domain model** section. The following entities are relevant for signature version 4 headers authentication: @@ -311,7 +311,7 @@ It takes the following parameters as input: * A `Credentials` object * A `ENUM_Region` enumeration value -The output is a boolean indicating whether the credentials are valid. +The output is a Boolean indicating whether the credentials are valid. ## Read More diff --git a/content/en/docs/appstore/use-content/platform-supported-content/modules/genai/genai-commons.md b/content/en/docs/appstore/use-content/platform-supported-content/modules/genai/genai-commons.md index 0936e93e391..11b577457b2 100644 --- a/content/en/docs/appstore/use-content/platform-supported-content/modules/genai/genai-commons.md +++ b/content/en/docs/appstore/use-content/platform-supported-content/modules/genai/genai-commons.md @@ -54,7 +54,7 @@ The technical purpose of GenAI Commons module is to define a common domain model ### Domain Model {#domain-model} -The domain model in Mendix is a data model that describes the information in your application domain in an abstract way. For more general information, see the [Domain Model](/refguide/domain-model/) documentation. To learn about where the entities from the domain model are used and relevant during implementation, see the [Microflows](#microflows) section below. +The domain model in Mendix is a data model that describes the information in your application domain in an abstract way. For more general information, see the [Data in the Domain Model](/refguide/domain-model/) documentation. To learn about where the entities from the domain model are used and relevant during implementation, see the [Microflows](#microflows) section below. {{< figure src="/attachments/appstore/platform-supported-content/modules/genai/genaicommons/genai-commons-domain-model.png" alt="" >}} @@ -250,7 +250,7 @@ An optional input object for the image generations operations to set optional re | `Height` | This determines the height of the image. | | `Width` | This determines the width of the image. | | `NumberOfImages` | This determines the number of images to be generated. | -| `Seed` | This can be used to influence the randomness of the generation. Ensures the reproducability and consistency in the generated images by controlling the initial state of the random number generator. | +| `Seed` | This can be used to influence the randomness of the generation. Ensures the reproducibility and consistency in the generated images by controlling the initial state of the random number generator. | | `CfgScale` | This can be used to influence the randomness of the generation. Adjusts the balance between adherence to the prompt and creative randomness in the image generation process. | | `ImageGenerationType` | This describes the type of image generation. Currently only text to image is supported. For more information, see [ENUM_ImageGenerationType](#enum-imagegenerationtype). | @@ -655,7 +655,7 @@ This Java action adds a new [KnowledgeBaseChunk](#knowledgebasechunk-entity) to | `InputText` | String | mandatory | Input text to generate an embedding vector for. | | `HumanReadableID` | String | mandatory | This is a front-end identifier that can be used for showing or retrieving sources in a custom way. If it is not relevant, "empty" must be passed explicitly here. | | `MxObject` | Type parameter | optional | This parameter is used to capture the Mendix object to which the chunk refers. This can be used for finding back the record in the Mendix database later on after the retrieval step. | -| `MetadataCollection` | [MetadataCollection](#metadatacollection-entity) | optional | This is an optional MetadataCollection that contains extra information about the KnowledgeBaaseChunk. Any key-value pairs can be stored. In the retrieval operations it is possible to filter on one or multiple metadata key-value pairs. | +| `MetadataCollection` | [MetadataCollection](#metadatacollection-entity) | optional | This is an optional MetadataCollection that contains extra information about the KnowledgeBaseChunk. Any key-value pairs can be stored. In the retrieval operations it is possible to filter on one or multiple metadata key-value pairs. | ###### Return Value @@ -703,7 +703,7 @@ This microflow creates a new [MetadataCollection](#metadatacollection-entity) an | Name | Type | Mandatory | Description | |--- |--- |--- |--- | -| `Key` | String | madatory | This is the name of the metadata and typically tells how the value should be interpreted. | +| `Key` | String | mandatory | This is the name of the metadata and typically tells how the value should be interpreted. | | `Value` | String | mandatory | This is the value of the metadata that provides additional information about the chunk in the context of the given key. | ###### Return Value diff --git a/content/en/docs/appstore/use-content/platform-supported-content/modules/sap/sap-event-mesh-connector.md b/content/en/docs/appstore/use-content/platform-supported-content/modules/sap/sap-event-mesh-connector.md index 53e9a6eb951..7dbc725f929 100644 --- a/content/en/docs/appstore/use-content/platform-supported-content/modules/sap/sap-event-mesh-connector.md +++ b/content/en/docs/appstore/use-content/platform-supported-content/modules/sap/sap-event-mesh-connector.md @@ -65,7 +65,7 @@ To help you work with the SAP Event Mesh connector, the following sections of th ### Domain Model {#domain-model} -The domain model is a data model that describes the information in your application domain in an abstract way. For more information, see [Domain Model](/refguide/domain-model/). +The domain model is a data model that describes the information in your application domain in an abstract way. For more information, see [Data in the Domain Model](/refguide/domain-model/). The entities in the table below describe all generalizations. These are reused by the different models for the specific microflow activities or for storing connection details. diff --git a/content/en/docs/appstore/use-content/platform-supported-content/modules/sap/sap-odata-connector.md b/content/en/docs/appstore/use-content/platform-supported-content/modules/sap/sap-odata-connector.md index 49bf02ef923..ffcd47ca274 100644 --- a/content/en/docs/appstore/use-content/platform-supported-content/modules/sap/sap-odata-connector.md +++ b/content/en/docs/appstore/use-content/platform-supported-content/modules/sap/sap-odata-connector.md @@ -778,7 +778,7 @@ This documentation describes two different domain models. 2. The OData Connector for SAP solutions Domain Model – this contains entities which are used by the OData Connector for SAP solutions itself: for example, to construct the request which needs to be sent to the OData service. A description of this domain model is included for completeness in the section [OData Connector for SAP Solutions Domain Model](#ConnectorDM) -For more information on domain models, see [Domain Model](/refguide/domain-model/). +For more information on domain models, see [Data in the Domain Model](/refguide/domain-model/). #### SAP Service Domain Model @@ -868,7 +868,7 @@ This domain model is part of the OData Connector for SAP solutions module and ca * [Attributes](/refguide/attributes/) * [Data Types](/refguide/data-types/) -* [Domain Model](/refguide/domain-model/) +* [Data in the Domain Model](/refguide/domain-model/) * [Entities](/refguide/entities/) * [SAP Cloud Connector](/developerportal/deploy/sap-cloud-platform/sap-cloud-connector/) * [SAP Help Portal](https://help.sap.com) diff --git a/content/en/docs/appstore/use-content/platform-supported-content/modules/snowflake/snowflake-rest-sql.md b/content/en/docs/appstore/use-content/platform-supported-content/modules/snowflake/snowflake-rest-sql.md index 92e6c0e99bc..6dd383ca26f 100644 --- a/content/en/docs/appstore/use-content/platform-supported-content/modules/snowflake/snowflake-rest-sql.md +++ b/content/en/docs/appstore/use-content/platform-supported-content/modules/snowflake/snowflake-rest-sql.md @@ -106,7 +106,7 @@ To help you work with the Snowflake REST SQL connector, the following sections o ### Domain Model {#domain-model} -The domain model is a data model that describes the information in your application domain in an abstract way. For more information, see [Domain Model](/refguide/domain-model/). +The domain model is a data model that describes the information in your application domain in an abstract way. For more information, see [Data in the Domain Model](/refguide/domain-model/). #### ConnectionDetails {#connection-details} diff --git a/content/en/docs/appstore/use-content/platform-supported-content/services/use-sap-model-creator.md b/content/en/docs/appstore/use-content/platform-supported-content/services/use-sap-model-creator.md index 0313edd4290..bf7c53980ed 100644 --- a/content/en/docs/appstore/use-content/platform-supported-content/services/use-sap-model-creator.md +++ b/content/en/docs/appstore/use-content/platform-supported-content/services/use-sap-model-creator.md @@ -37,7 +37,7 @@ The Data Model module contains up to four resources which help to consume the OD #### Domain Model -Each Mendix SAP data model has a domain model that describes the information in the OData service. The domain model consists of entities and their relations represented by associations. For more information, see [Domain Model](/refguide/domain-model/). +Each Mendix SAP data model has a domain model that describes the information in the OData service. The domain model consists of entities and their relations represented by associations. For more information, see [Domain Model]. {{< figure src="/attachments/appstore/platform-supported-content/services/sap-model-creator/sap-service-example.png" class="no-border" >}} diff --git a/content/en/docs/deployment/private-cloud/reduced-downtime-deployment.md b/content/en/docs/deployment/private-cloud/reduced-downtime-deployment.md index 4e2aac0de69..d69e2fe6653 100644 --- a/content/en/docs/deployment/private-cloud/reduced-downtime-deployment.md +++ b/content/en/docs/deployment/private-cloud/reduced-downtime-deployment.md @@ -6,7 +6,7 @@ weight: 35 --- ## Introduction -Kubernetes allows to update an app without downtime by [performing a rolling update](https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/). Instead of stopping an app and then starting it with an updated version or configuration, Kubernetes can replace pods (replicas) one by one with an updated version. Existing pods handle requests until the newer version is fully started. Any changes in the [domain model](/refguide/domain-model/) need a database (schema) update. While the update process runs, you cannot modify any persistent entities. +Kubernetes allows to update an app without downtime by [performing a rolling update](https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/). Instead of stopping an app and then starting it with an updated version or configuration, Kubernetes can replace pods (replicas) one by one with an updated version. Existing pods handle requests until the newer version is fully started. Any changes in the [Data in the Domain Model](/refguide/domain-model/) need a database (schema) update. While the update process runs, you cannot modify any persistent entities. The Private Cloud Operator uses a [recreate](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#recreate-deployment) strategy by default. That is, the current version (configuration) of an app stops, and then the new version starts. Alternatively, the Private Cloud Operator can use a **PreferRolling** strategy. That is, the Operator tries to perform a rolling update whenever possible. If the Operator detects that a database schema update is needed, it switches to a Recreate strategy to perform a full restart. If the new version of the app has model changes, deploying it requires a schema update. In this case, the Private Cloud Operator automatically stops all replicas of the app, causing downtime. @@ -44,9 +44,9 @@ The following changes in the UI can be done without downtime, but as soon as the * Page changes, including layout or CSS changes * Changes in nanoflows or microflow parameters, if the microflow is used on a page -* Changes in Javascript actions +* Changes in JavaScript actions -Thes following changes will be deployed with downtime, because the model must be updated: +The following changes will be deployed with downtime, because the model must be updated: * Adding Marketplace modules that have persistent entities * Updating the object model in the app itself, or its Marketplace modules diff --git a/content/en/docs/refguide/modeling/consistency-errors/_index.md b/content/en/docs/refguide/modeling/consistency-errors/_index.md index 84334938496..7478d6410e5 100644 --- a/content/en/docs/refguide/modeling/consistency-errors/_index.md +++ b/content/en/docs/refguide/modeling/consistency-errors/_index.md @@ -31,7 +31,7 @@ Errors need to be solved before your app can be deployed. A consistency error ca * [Navigation](/refguide/consistency-errors-navigation/) * [Microflows](/refguide/microflows/) * [Workflows](/refguide/workflows/) -* [Domain Model](/refguide/domain-model/) +* [Data in the Domain Model](/refguide/domain-model/) * [Integration](/refguide/integration/) * [Security](/refguide/security/) diff --git a/content/en/docs/refguide/modeling/domain-model/_index.md b/content/en/docs/refguide/modeling/domain-model/_index.md index 33081d36176..0c30d5df42c 100644 --- a/content/en/docs/refguide/modeling/domain-model/_index.md +++ b/content/en/docs/refguide/modeling/domain-model/_index.md @@ -1,6 +1,6 @@ --- -title: "Domain Model" -linktitle: "Domain Model (Data)" +title: "Data in the Domain Model" +linktitle: "Data (Domain Model)" url: /refguide/domain-model/ weight: 30 description: "Introduces the domain model in Studio Pro." diff --git a/content/en/docs/refguide/modeling/resources/data-sets/_index.md b/content/en/docs/refguide/modeling/domain-model/entities/data-sets.md similarity index 99% rename from content/en/docs/refguide/modeling/resources/data-sets/_index.md rename to content/en/docs/refguide/modeling/domain-model/entities/data-sets.md index 79442caeb06..77239575f78 100644 --- a/content/en/docs/refguide/modeling/resources/data-sets/_index.md +++ b/content/en/docs/refguide/modeling/domain-model/entities/data-sets.md @@ -1,7 +1,7 @@ --- title: "Datasets" url: /refguide/data-sets/ -weight: 50 +weight: 80 #If moving or renaming this doc file, implement a temporary redirect and let the respective team know they should update the URL in the product. See Mapping to Products for more details. --- diff --git a/content/en/docs/refguide/modeling/domain-model/entities/indexes.md b/content/en/docs/refguide/modeling/domain-model/entities/indexes.md index b4f2456192c..a0a123883de 100644 --- a/content/en/docs/refguide/modeling/domain-model/entities/indexes.md +++ b/content/en/docs/refguide/modeling/domain-model/entities/indexes.md @@ -25,7 +25,7 @@ Indexes are ordered, which means that when you create an index on two or more at ### Indexes on System Members -If you choose to store an entity's `owner` and `changedBy` system members, an index is created. This is not so for the `createdDate` and `changedDate` system members. In addition, an index is created for the automatically generated attribute `id`. See [Domain Model](/refguide/domain-model/) for more information about the implementation of these attributes. +If you choose to store an entity's `owner` and `changedBy` system members, an index is created. This is not so for the `createdDate` and `changedDate` system members. In addition, an index is created for the automatically generated attribute `id`. See [Data in the Domain Model](/refguide/domain-model/) for more information about the implementation of these attributes. ### Indexes on Non-Persistable Entities diff --git a/content/en/docs/refguide/modeling/domain-model/oql/_index.md b/content/en/docs/refguide/modeling/domain-model/oql/_index.md new file mode 100644 index 00000000000..9d2cc20a5ce --- /dev/null +++ b/content/en/docs/refguide/modeling/domain-model/oql/_index.md @@ -0,0 +1,77 @@ +--- +title: "OQL" +url: /refguide/oql/ +weight: 90 +--- + +## Introduction + +The Mendix Object Query Language (OQL) is a relational query language inspired by [SQL](https://en.wikipedia.org/wiki/Sql). The major advantage of OQL is that it uses Mendix entity and association names instead of actual database table names. In that way, it is possible to create queries that use names from the data model of your Mendix app without thinking about how that data model is represented in the database. + +In addition, OQL can use predefined relations (associations) to easily join objects without having to calculate which columns should be coupled. Despite these differences, many SQL keywords also work in OQL. + +Some examples of OQL queries are: + +* `SELECT LastName FROM Sales.Customer` – retrieves the family names of all customers +* `SELECT FirstName FROM Sales.Customer WHERE LastName = 'Jansen'` – retrieves the given name of all customers with family name "Jansen" +* `SELECT SUM(TotalAmount) FROM Sales."Order" WHERE IsPaid = true` – retrieves the sum of the total amount on all paid orders (`Order` needs to be wrapped in quotes, see the [Reserved Words](#reserved-oql-words) section below) + +{{% alert color="info" %}} +OQL queries do not take security into account out-of-the-box. This means that you can use OQL to manually define custom security expressions. In some cases, handling security yourself using OQL—instead of using the out-of-the-box security of XPath—may result in faster queries. +{{% /alert %}} + +{{% alert color="info" %}} +You can try your OQL example online in the [OQL Playground](https://service.mendixcloud.com/p/OQL) demo app. +{{% /alert %}} + +## Reserved Words {#reserved-oql-words} + +Words with a specific purpose in OQL are reserved. If you use reserved words for entity, variable, or attribute names in an OQL query, they must be wrapped in double quotes `" "`. For example, in the OQL query `SELECT AVG(TotalPrice) FROM Sales."Order" WHERE IsPaid = 1`, `Order` needs to be wrapped in quotes because it is a reserved word, as it can be used to `ORDER BY`. + +Here is a list of all OQL reserved words: + +`ALL`, `AND`, `AS`, `ASC`, `AVG` + +`BOOLEAN`, `BY` + +`CASE`, `CAST`, `COUNT` + +`DATEDIFF`, `DATEPART`, `DATETIME`, `DAY`, `DAYOFYEAR`, `DECIMAL`, `DESC`, `DISTINCT` + +`ELSE`, `END`, `EXISTS` + +`FALSE`, `FLOAT`, `FROM`, `FULL` + +`GROUP` + +`HAVING`, `HOUR` + +`IN`, `INNER`, `INTEGER`, `IS` + +`JOIN` + +`LEFT`, `LIKE`, `LIMIT`, `LONG` + +`MAX`, `MILLISECOND`, `MIN`, `MINUTE`, `MONTH` + +`NOT`, `NULL` + +`OFFSET`, `ON`, `OR`, `ORDER`, `OUTER` + +`QUARTER` + +`RIGHT` + +`SECOND`, `SELECT`, `STRING`, `SUM` + +`THEN`, `TRUE` + +`UNION` + +`WEEK`, `WEEKDAY`, `WHEN`, `WHERE` + +`YEAR` + +{{% alert color="info" %}} +Mendix does not support Float data type since version 8. In OQL, `FLOAT` is a reserved word for legacy reasons. It should not be used. +{{% /alert %}} diff --git a/content/en/docs/refguide/modeling/domain-model/oql/oql-clauses.md b/content/en/docs/refguide/modeling/domain-model/oql/oql-clauses.md new file mode 100644 index 00000000000..1cbcb99ea8b --- /dev/null +++ b/content/en/docs/refguide/modeling/domain-model/oql/oql-clauses.md @@ -0,0 +1,1423 @@ +--- +title: "OQL Clauses" +url: /refguide/oql-clauses/ +weight: 10 +aliases: + - /refguide/oql-from-clause/ + - /refguide/oql-group-by-clause/ + - /refguide/oql-limit-clause/ + - /refguide/oql-order-by-clause/ + - /refguide/oql-select-clause/ + - /refguide/oql-subqueries/ + - /refguide/oql-where-clause/ +--- + +## Introduction + +OQL clauses are reserved words that structure an OQL query into sections. Each section, and therefore clause, is responsible for a different aspect of the query. Every query has two mandatory clauses: `SELECT` and `FROM`. + +For example: + +```sql +SELECT LastName, Address +FROM Sales.Customer +``` + +These basic clauses define the data that needs to be retrieved and which entity or entities should be used as a source. Other clauses are not mandatory. They are used to specify limitations and other rules on the data being retrieved. + +Clauses must be presented in the following order, but can be left out if they are optional: + +1. [`SELECT`](#select) +2. [`FROM`](#from) +3. [`WHERE`](#where) +4. [`GROUP BY`](#group-by) +5. [`HAVING`](#having) (allowed only in combination with `GROUP BY`) +6. [`ORDER BY`](#order-by) +7. [`LIMIT`](#limit-offset) +8. [`OFFSET`](#limit-offset) + +The `UNION` clause defies the usual order presented above. It will be presented in a [Union Clause](#oql-union) section at the end. + +## `SELECT` Clause {#select} + +The `SELECT` clause specifies which entity attributes or other specified data must be retrieved. The clause returns all the requested values of objects which match the `SELECT` clause. + +The `SELECT` clause consists of the term `SELECT` and one or more column definitions. Each column definition must be separated by a comma. Each column definition defines a column or a set of columns in the result. Each single value column definition can have an alias, which will be the name of the column in the result. + +{{% alert color="info" %}} +We use terms "attributes" and "columns" for data in different contexts. Attributes are the [entity attributes](/refguide/attributes/). If data belongs to objects of an entity, we refer to that data as attributes of those objects. While columns contain the same data, they are not tied to a particular entity. In the general case, OQL does not result in an entity, and so results on an OQL query are columns. When it comes to view entities, OQL columns are mapped to attributes of a view entity, and then we can speak of attributes again. + +Similarly, we use term "objects" when referring to objects of an entity, but for results of an OQL query, we use "rows" because the results are not necessarily mapped to objects of a particular entity. +{{% /alert %}} + +### Syntax + +The syntax is as follows: + +```sql +SELECT [ DISTINCT ] + { + * + | { entity_name | from_alias }.* + | { expression [ [ AS ] column_alias ] } [ ,...n ] + } +``` + +### Selecting Specific Attributes + +A simple way to define what is to be retrieved is to specify particular attributes of an entity. For example: + +```sql +SELECT FirstName AS FName, LastName AS LName +FROM Sales.Customer +``` + +It is possible to specify aliases for columns – in the example above: `FirstName AS FName`. The keyword `AS` is not mandatory, so the following query is equivalent to the one above: + +```sql +SELECT FirstName FName, LastName LName +FROM Sales.Customer +``` + +An alias is an alternative name which replaces the column name in the result. For example, when the `name` attribute is retrieved, the result column is "Name". With an alias, you can call the result column something else, like "Customer_Name". An alias cannot contain spaces. + +{{% todo %}}Add link to view entity documentation{{% /todo %}} +{{% alert color="info" %}} +Aliases are mandatory when defining a view entity. +{{% /alert %}} + +### Selecting All Attributes Using `*` + +Using `*` (asterisk) in the SELECT clause specifies that the values of all attributes from all entities in the `FROM` clause should be returned. + +Specifying `entity_name/*` and `from_alias/*` specify that the values of all attributes of the specified entity or expression of the `FROM` clause should be returned. + +`entity_name` can optionally be put in double quotes. If the entity name is a [reserved OQL word](/refguide/oql-clauses/#reserved-oql-words) (like `Order` or `Group`), double quotes are mandatory. + +{{% alert color="info" %}} +Specifying all attributes will also return attributes which are normally hidden in the Domain Model, such as the `ID` of each object. +{{% /alert %}} + +#### Examples + +The following query returns the values of all the attributes of `Sales.Customer` + +```sql +SELECT * +FROM Sales.Customer +``` + +The following query returns all attributes of objects of `Sales.Request` that are associated with `Sales.Customer` objects (see [Select from Multiple Tables using `JOIN`](/refguide/oql-clauses/#join)) + +``` +SELECT Sales.Request/* +FROM Sales.Customer +JOIN Sales.Customer/Sales.Customer_Request/Sales.Request +``` + +The following query is equivalent to the previous one, but it uses table aliases + +``` +SELECT Req/* +FROM Sales.Customer Cust +JOIN Cust/Sales.Customer_Request/Sales.Request Req +``` + +### Selecting Distinct Values with `DISTINCT` {#distinct} + +The keyword `DISTINCT` specifies that duplicate rows must not be included in the result. If used, `DISTINCT` should follow directly after `SELECT`. It is not possible to request only some attributes to be distinct. `Null` is treated as a separate value. If the query result contains multiple `Null` values, `DISTINCT` is applied to them the same way it would be applied to other values. + +In this example, the `Sales.Customer` entity has 4 objects. + +```sql +SELECT FirstName FName, LastName LName +FROM Sales.Customer +``` + +returns + +| FName | LName | +| ----- | ----- | +| John | Doe | +| Jane | Doe | +| Jane | Doe | +| Jane | Moose | + +The following query, using `DISTINCT` will result only in unique last names + +```sql +SELECT DISTINCT LastName LName +FROM Sales.Customer +``` + +returns + +| LName | +| ----- | +| Doe | +| Moose | + +If multiple attributes are selected, the result is all the unique combinations that are present in the database: + +```sql +SELECT DISTINCT FirstName FName, LastName LName +FROM Sales.Customer +``` + +returns + +| FName | LName | +| ----- | ----- | +| John | Doe | +| Jane | Doe | +| Jane | Moose | + +`DISTINCT` can also be combined with `*`. + +{{% alert color="info" %}} +If you specify an entity name in `FROM`, this will return all columns, including the unique column `ID`. This means that adding `DISTINCT` does not affect the result. + +In more complex cases, `SELECT DISTINCT *` can become useful. +{{% /alert %}} + +```sql +SELECT DISTINCT * +FROM Sales.Customer +``` + +returns + +| ID | FName | LName | +| --------------- | ----- | ----- | +| 562949953421521 | John | Doe | +| 562949953421683 | Jane | Doe | +| 562949953421777 | Jane | Doe | +| 562949953421923 | Jane | Moose | + +### Expressions + +It is possible to use more complex expressions in `SELECT`. This is explained in detail in [OQL Expressions](/refguide/oql-expressions/). + +It is also possible to use a subquery. See [Subquery in `SELECT`](/refguide/oql-clauses/#subquery-in-select) for more details. + +### Selecting Attributes over Associations + +A unique feature of OQL is the ability to access attributes of associated objects using paths. For example: + +```sql +SELECT + Number AS RequestNumber, + Sales.Request/Sales.Request_Customer/Sales.Customer/LastName AS CustomerName +FROM Sales.Request +``` + +It is possible to build paths over multiple associations. Associated entities can be reached from both directions. System associations `System.owner` and `System.changedBy` can also be used in such association paths, assuming they are enabled for the entity. For example: + +```sql +SELECT + LastName AS CustomerName, + Sales.Customer/Sales.Request_Customer/Sales.Request/System.owner/System.User/Name AS UserName +FROM Sales.Customer +``` + +## `FROM` Clause {#from} + +The `FROM` clause specifies the entities or other source or sources from which the data must be retrieved. + +This clause starts with the `FROM` keyword, followed by an entity name. To select data from additional entities, add these entities via the `JOIN` keyword. In OQL, this syntax is a little more strict than that of the SQL `FROM` clause. + +### Syntax + +Below is the full syntax of the FROM clause: + +```sql +FROM + { + entity_name | ( sub_oql_query ) + } + [ [ AS ] from_alias ] + + { + { INNER | { { LEFT | RIGHT | FULL } [ OUTER ] } } JOIN + entity_path | entity_name | ( sub_oql_query ) [ [ AS ] from_alias ] + [ ON ] + } [ ,...n ] +``` + +### Select from One Entity + +Below is an example of a simple select from one entity: + +```sql +SELECT Sales.Customer/LastName +FROM Sales.Customer +``` + +You can use an alias to replace the entity name: + +```sql +SELECT Cust/LastName +FROM Sales.Customer AS Cust +``` + +### Select from Multiple Entities + +You can specify multiple tables in `FROM`. + +{{% alert color="info" %}} +In these examples there are 2 entities, `Sales.Customer` and `Sales.Request`: + +```sql +SELECT * +FROM Sales.Customer +``` + +| ID | FirstName | LastName | +| --------------- | ----- | ----- | +| 562949953421521 | John | Doe | +| 562949953421923 | Jane | Moose | +| 562949953422131 | Jim | Elk | + +```sql +SELECT * +FROM Sales.Request +``` + +| ID | CustomerName | Number | +| ---------------- | ------------ | ------ | +| 1688849860264073 | Doe | 1 | +| 1688849860264231 | Moose | 2 | +| 1688849860264654 | Caribou | -1 | + +{{% /alert %}} +You can select data from both entities at once. + +```sql +SELECT * +FROM Sales.Customer, Sales.Request +``` + + This returns get all attributes of both entities. Every object of the first entity is combined with every object of the second entity, which results in a cartesian product of objects of the two entities. + +| sales$customer.ID | FirstName | LastName | sales$request.ID | CustomerName | Number | +| --------------- | ----- | ----- | ---------------- | ------------ | ------ | +| 562949953421521 | John | Doe | 1688849860264073 | Doe | 1 | +| 562949953421923 | Jane | Moose | 1688849860264073 | Doe | 1 | +| 562949953422131 | Jim | Elk | 1688849860264073 | Doe | 1 | +| 562949953421521 | John | Doe | 1688849860264231 | Moose | 2 | +| 562949953421923 | Jane | Moose | 1688849860264231 | Moose | 2 | +| 562949953422131 | Jim | Elk | 1688849860264231 | Moose | 2 | +| 562949953421521 | John | Doe | 1688849860264654 | Caribou | -1 | +| 562949953421923 | Jane | Moose | 1688849860264654 | Caribou | -1 | +| 562949953422131 | Jim | Elk | 1688849860264654 | Caribou | -1 | + +You can specify conditions to filter the results in a `WHERE` clause (see more details and examples in [`WHERE` Clause](#where), below) + +```sql +SELECT * +FROM Sales.Customer Cust, Sales.Request Req +WHERE Cust.LastName = Req.CustomerName +``` + +returns + +| sales$customer.ID | FirstName | LastName | sales$request.ID | CustomerName | Number | +| --------------- | ----- | ----- | ---------------- | ------------ | ------ | +| 562949953421521 | John | Doe | 1688849860264073 | Doe | 1 | +| 562949953421923 | Jane | Moose | 1688849860264231 | Moose | 2 | + +To avoid ambiguity in case of duplicate attribute names, you should use the entity name or alias when specifying columns to be selected. The format in that case is, respectively, `./` or `/`. You can also retrieve all attributes of a particular entity using `/*`. For example: + +[//]: # (Specifying sql here causes issues as it doesn't understand the "*" and starts using italics!) + +``` +SELECT Cust/FirstName, Req/* +FROM Sales.Customer Cust, Sales.Request Req +WHERE Cust.LastName = Req.CustomerName +``` + +returns + +| FirstName | sales$request.ID | CustomerName | Number | +| ----- | ---------------- | ------------ | ------ | +| John | 1688849860264073 | Doe | 1 | +| Jane | 1688849860264231 | Moose | 2 | + +### Select from Multiple Tables using `JOIN` {#join} + +OQL supports the `JOIN` syntax, which is similar to SQL. There are 4 main types of `JOIN`: + +* `INNER JOIN` or simply `JOIN` +* `LEFT JOIN` or `LEFT OUTER JOIN` +* `RIGHT JOIN` or `RIGHT OUTER JOIN` +* `FULL JOIN` or `FULL OUTER JOIN` + +In addition to standard SQL syntax, OQL supports joins over associations. + +The syntax is as follows: + +```sql +{ INNER | { { LEFT | RIGHT | FULL } [ OUTER ] } } JOIN +entity_path | entity_name | ( sub_oql_query ) [ [ AS ] from_alias ] +[ ON ] +``` + +#### entity_path + +`entity_path` specifies the entity to join and the path to this entity from an earlier defined entity in the `FROM` clause. + +For example, the path `Crm.Customer/Crm.Customer_Address/Crm.Address` defines an association path from the entity **Crm.Customer** to an associated entity **Crm.Address**. + +As with `entity_name`, double quotes can be used. + + If `entity_path` is specified, the `ON` condition is optional. If entities are joined by name without using an association path, the `ON` condition is mandatory. + +### `JOIN` Types + +#### `INNER JOIN` + +An `INNER JOIN` is the most common join operation between entities and represents the default join type. The query compares each row of entity A with each row of entity B to find all the pairs of rows that have an association and/or satisfy the JOIN predicate. If the association exists and the JOIN predicate is satisfied, the column values for each matched pair of rows of A and B are combined into a resulting row. + +Example of an `INNER JOIN` with predicate: + +```sql +SELECT Cust/LastName, Req/Number +FROM Sales.Customer Cust +JOIN Sales.Request Req ON Cust.LastName = Req.CustomerName +``` + +| LastName | Number | +| ------------ | ------ | +| Doe | 1 | +| Moose | 2 | + +If the model contains an association between `Sales.Request` and `Sales.Customer`, a more logical way to write this query would be to use a join on association: + +```sql +SELECT Cust/LastName, Req/Number +FROM Sales.Customer Cust +JOIN Cust/Sales.Request_Customer/Sales.Request Req +``` + +| LastName | Number | +| ------------ | ------ | +| Doe | 1 | +| Moose | 2 | + +##### `LEFT OUTER JOIN` + +A `LEFT OUTER JOIN` query compares each row of entity A with each row of entity B to find all pairs of rows which have an association and thus satisfy the `JOIN` predicate. When the association exists and the `JOIN` predicate is satisfied, column values for each matched pair of rows of A and B are combined into a resulting row. + +However, in contrast to the `INNER JOIN` construction, the query will also return rows of entity A which do not match entity B. When columns of entity B are specified, these columns contain a null value for these rows. + +The syntax is as follows: + +```sql +LEFT [ OUTER ] JOIN entity_path [ ON ] +``` + +Example of a `LEFT OUTER JOIN` with predicate: + +```sql +SELECT Cust/LastName, Req/Number +FROM Sales.Customer Cust +LEFT OUTER JOIN Sales.Request Req ON Cust.LastName = Req.CustomerName +``` + +| LastName | Number | +| ------------ | ------ | +| Doe | 1 | +| Moose | 2 | +| Elk | Null | + +`LEFT OUTER JOIN` over association is also supported: + +```sql +SELECT Cust/LastName, Req/Number +FROM Sales.Customer Cust +LEFT OUTER JOIN Cust/Sales.Request_Customer/Sales.Request Req +``` + +| LastName | Number | +| ------------ | ------ | +| Doe | 1 | +| Moose | 2 | +| Elk | NULL | + +##### `RIGHT OUTER JOIN` + +A `RIGHT OUTER JOIN` query compares each row of entity A with each row of entity B to find all pairs of rows which have an association and thus satisfy the `JOIN` predicate. If the association exists and the `JOIN` predicate is satisfied, the column values for each matched pair of rows of A and B are combined into a resulting row. + +However, in contrast to the `INNER JOIN` construction, rows from entity B that do not match entity A will also be returned. When columns of entity A are specified, these columns contain a null value for these rows. + +The syntax is as follows: + +```sql +RIGHT [ OUTER ] JOIN entity_path [ ON ] +``` + +Example of a `RIGHT OUTER JOIN` with predicate: + +```sql +SELECT Cust/LastName, Req/Number +FROM Sales.Customer Cust +RIGHT OUTER JOIN Sales.Request Req ON Cust.LastName = Req.CustomerName +``` + +| LastName | Number | +| ------------ | ------ | +| Doe | 1 | +| Moose | 2 | +| NULL | -1 | + +`RIGHT OUTER JOIN` over association is also supported: + +```sql +SELECT Cust/LastName, Req/Number +FROM Sales.Customer Cust +RIGHT OUTER JOIN Cust/Sales.Request_Customer/Sales.Request Req +``` + +| LastName | Number | +| ------------ | ------ | +| Doe | 1 | +| Moose | 2 | +| NULL | -1 | + +##### `FULL OUTER JOIN` + +A `FULL OUTER JOIN` query compares each row of entity A with each row of entity B to find all pairs of rows which have an association and thus satisfy the `JOIN` predicate. When the association exists and the `JOIN` predicate is satisfied, column values for each matched pair of rows from A and B are combined into a result row. + +However, in contrast to the `INNER JOIN` construction, data from entities that do *not* match will also be returned. For these rows, columns of missing entities will contain null values. + +The syntax is as follows: + +```sql +FULL [ OUTER ] JOIN entity_path [ ON ] +``` + +Example of a `FULL OUTER JOIN` with predicate: + +```sql +SELECT Cust/LastName, Req/Number +FROM Sales.Customer Cust +FULL OUTER JOIN Sales.Request Req ON Cust.LastName = Req.CustomerName +``` + +| LastName | Number | +| ------------ | ------ | +| Doe | 1 | +| Moose | 2 | +| Elk | NULL | +| NULL | -1 | + +`FULL OUTER JOIN` over association is also supported: + +```sql +SELECT Cust/LastName, Req/Number +FROM Sales.Customer Cust +FULL OUTER JOIN Cust/Sales.Request_Customer/Sales.Request Req +``` + +| LastName | Number | +| ------------ | ------ | +| Doe | 1 | +| Moose | 2 | +| Elk | NULL | +| NULL | -1 | + +### Select from a Subquery + +See [Subquery in `FROM`](/refguide/oql-clauses/#subquery-in-from) for details. + +## `WHERE` Clause {#where} + +The `WHERE` clause specifies how the data being retrieved must be constrained. + +### Syntax + +The syntax is as follows: + +```sql +WHERE +``` + +`` is an expression of type BOOLEAN. Expressions can consist of simple comparisons using operators, functions, keywords, parameters, and system variables. If the result of the expression is `True` for a particular row, that row is included in the result. Rows that do not match the expression are not included in the result. + +For more information, see [OQL Expressions](/refguide/oql-expressions/). + +### Examples + +The following query retrieves all customers whose name is equal to "Doe": + +```sql +SELECT FirstName, LastName +FROM Sales.Customer +WHERE LastName = 'Doe' +``` + +| FirstName | LastName | +| --------- | -------- | +| John | Doe | + +It is possible to specify multiple conditions in a constraint using logical operators `AND` and `OR`. + +{{% alert color="info" %}} +Note that `AND` has precedence over `OR` +{{% /alert %}} + +For example: + +```sql +SELECT CustomerName, Number +FROM Sales.Request +WHERE + CustomerName = 'Doe' + OR + CustomerName != 'Doe' + AND + Number < 0 +``` + +| CustomerName | Number | +| ------------ | ------ | +| Doe | 1 | +| Caribou | -1 | + +You can modify the precedence of logical operators using parentheses: + +```sql +SELECT CustomerName, Number +FROM Sales.Request +WHERE + ( + CustomerName = 'Doe' + OR + CustomerName != 'Doe' + ) + AND + Number < 0 +``` + +| CustomerName | Number | +| ------------ | ------ | +| Caribou | -1 | + +You can also use a subquery in `WHERE`. See [Subquery in `WHERE`](/refguide/oql-clauses/#subquery-in-where) for examples. + +#### Entities over Associations in `WHERE` Clause + +A feature that is specific to OQL and is not in the standard SQL syntax is using paths to other entities over associations. + +For example, when selecting from entity `Sales.Customer`, we can access an attribute of the associated entity `Sales.Request`: + +```sql +SELECT FirstName, LastName +FROM Sales.Customer +WHERE + Sales.Customer/Sales.Request_Customer/Sales.Request/Number = 1 +``` + +| FirstName | LastName | +| --------- | -------- | +| John | Doe | + +#### `WHERE` Clause Returns `NULL` + +In some cases, the expression in `WHERE` can result in `NULL`. In that case, the `WHERE` expression is equal to `FALSE`, and the query returns no rows. + +```sql +SELECT FirstName, LastName +FROM Sales.Customer +WHERE NULL +``` + +| FirstName | LastName | +| --------- | -------- | + +## `GROUP BY` Clause {#group-by} + +The `GROUP BY` clause groups OQL query results into summary rows based on the values of one or more attributes. You can filter aggregated results further using a `HAVING` clause. + +### Syntax + +The syntax is as follows: + +```sql +GROUP BY + expression [ ,...n ] + +[HAVING ] +``` + +{{% alert color="info" %}} +The `GROUP BY` clause is usually used in combination with [aggregations](/refguide/oql-expressions/#aggregates): `AVG`, `COUNT`, `MAX`, `MIN`, `SUM`. +{{% /alert %}} + +### Using `GROUP BY` + +When a query contains a `GROUP BY` clause, then its `SELECT` clause can contain only aggregate functions and attributes and other expressions used in `GROUP BY`. If an attribute is present in `SELECT`, but not present in `GROUP BY`, the query is invalid. + +{{% alert color="info" %}} +The examples below assume that there is an entity `Sales.Location` with the following objects: + +```sql +SELECT Brand, City, Stock, Address, LocationNumber +FROM Sales.Location +``` + +| Brand | City | Stock | Address | LocationNumber | +| ------ | ----- | ----- | --------- | ----- | +| Cinco | Rotterdam | 5 | Address 1 | 1 | +| Rekall | Utrecht | 9 | Address 4 | 4 | +| Rekall | Zwolle | 3 | Address 3 | 3 | +| Veidt | Rotterdam | 23 | Address 2 | 2 | +| Veidt | Rotterdam | 1 | Address 6 | 6 | +| Veidt | Utrecht | 2 | Address 5 | 5 | + +{{% /alert %}} + +#### `GROUP BY` with Single Aggregate + +The following query retrieves total stock per brand: + +```sql +SELECT Brand, SUM(Stock) AS SumStock +FROM Sales.Location +GROUP BY Brand +``` + +| Brand | SumStock | +| ------ | ----- | +| Cinco | 5 | +| Rekall | 12 | +| Veidt | 26 | + +#### `GROUP BY` with Multiple Aggregates + +You can specify multiple aggregate functions in combination with `GROUP BY`: + +```sql +SELECT + Brand, + SUM(Stock) AS SumStock, + MIN(Stock) AS MinStock, + MAX(Stock) AS MaxStock +FROM Sales.Location +GROUP BY Brand +``` + +| Brand | SumStock | MinStock | MaxStock | +| ------ | ----- | ----- | ----- | +| Cinco | 5 | 5 | 5 | +| Rekall | 12 | 3 | 9 | +| Veidt | 26 | 1 | 23 | + +#### `GROUP BY` Multiple Attributes + +You can also group by multiple attributes: + +```sql +SELECT Brand, City, SUM(Stock) AS SumStock +FROM Sales.Location +GROUP BY Brand, City +``` + +| Brand | City | SumStock | +| ------ | ------ | ----- | +| Cinco | Rotterdam | 5 | +| Rekall | Utrecht | 9 | +| Rekall | Zwolle | 3 | +| Veidt | Rotterdam | 24 | +| Veidt | Utrecht | 2 | + +#### Using Functions with `GROUP BY` + +You can use functions of attributes in the `SELECT` clause, but only if those attributes are present in the `GROUP BY` clause: + +```sql +SELECT Brand, LENGTH(Brand) AS NameLen, SUM(Stock) AS SumStock +FROM Sales.Location +GROUP BY Brand +``` + +| Brand | NameLen | SumStock | +| ------ | ------ | ----- | +| Cinco | 5 | 5 | +| Rekall | 6 | 12 | +| Veidt | 5 | 26 | + +You can also use functions of attributes in `GROUP BY`. Please note that if `GROUP BY` contains a function of an attribute, the attribute itself cannot be present in the `SELECT` clause because that would lead to ambiguity. + +```sql +SELECT LENGTH(Brand) AS NameLen, SUM(Stock) AS SumStock +FROM Sales.Location +GROUP BY LENGTH(Brand) +``` + +| NameLen | SumStock | +| ------ | ----- | +| 5 | 5 | +| 6 | 12 | +| 5 | 26 | + +{{% alert color="info" %}} +`GROUP BY` behavior in OQL relies on the behavior of the underlying database. Some functionality is allowed by OQL syntax, but its implementation differs per vendor. + +It is recommended not to use the following functionality to avoid potential migration problems. + +1. Aliases in `GROUP BY`. [Supported](/refguide/system-requirements/#databases) database vendors that allow do allow aliases in `GROUP BY` are HSQLDB, PostgreSQL, MariaDB, and MySQL. +2. Subqueries in `GROUP BY`. Supported database vendors which do allow subqueries in `GROUP BY` are PostgreSQL, MariaDB, and MySQL. +{{% /alert %}} + +### Using `GROUP BY` with `HAVING`{#having} + +The `HAVING` clause is used to filter aggregated results of `GROUP BY`. The difference between `HAVING` and `WHERE` is that `WHERE` is applied to every object before the objects are grouped, and `HAVING` is applied only to the aggregate rows. + +The following query returns only aggregate rows for brands with more than one location: + +```sql +SELECT Brand, SUM(Stock) AS SumStock, COUNT(*) AS LocationCount +FROM Sales.Location +GROUP BY Brand +HAVING COUNT(*) > 1 + +``` + +| Brand | SumStock | LocationCount | +| ------ | ----- | ---- | +| Rekall | 12 | 2 | +| Veidt | 26 | 3 | + +You can use aggregate functions that are not present in `SELECT`: + +```sql +SELECT Brand +FROM Sales.Location +GROUP BY Brand +HAVING COUNT(*) > 1 AND SUM(Stock) < 20 + +``` + +| Brand | +| ------ | +| Rekall | + +You can also use [more complex expressions](/refguide/oql-expression-syntax/) such as subqueries and functions in `HAVING`: + +```sql +SELECT Brand +FROM Sales.Location loc +GROUP BY Brand +HAVING + COUNT(*) > 1 + AND + EXISTS ( + SELECT * + FROM Sales.Storage stor + WHERE stor/AvailableStorage >= SUM(loc/Stock) + ) +``` + +## `ORDER BY` Clause{#order-by} + +The `ORDER BY` clause specifies the sort order used on columns returned in a `SELECT` statement. Multiple columns can be specified. Columns are ordered in the sequence of the items in the `ORDER BY` clause. + +{{% alert color="info" %}} +This clause can include items that do not appear in the `SELECT` clause, except when `SELECT DISTINCT` is specified or when a `GROUP BY` clause exists. When `UNION` is used, the column names or aliases must be those specified in the `SELECT` clause of the first part of the query. More information is presented in the [Union Clause](#oql-union) section. +{{% /alert %}} + +### Syntax + +The syntax is as follows: + +```sql +ORDER BY + { + order_by_expression [ ASC | DESC ] + } [ ,...n ] +``` + +### order_by_expression + +`order_by_expression` specifies an attribute of an entity or an alias from the `FROM` clause to sort on. + +For example: + +```sql +SELECT FirstName, LastName +FROM Sales.SalesPerson +ORDER BY LastName +``` + +| FirstName | LastName | +| ----- | ----- | +| John | Doe | +| Amelia | Doe | +| Oliver | Doe | +| Oliver | Moose | +| Jane | Moose | + +{{% alert color="info" %}} +For information on the default ordering behavior of NULL values, see the [NULL Values Order Behavior](/refguide/ordering-behavior/#null-ordering-behavior) section of *Order By Behavior*. +{{% /alert %}} + +{{% alert color="info" %}} +It is not possible to use functions of attributes in an `ORDER BY` clause. +{{% /alert %}} + +### ASC + +`ASC` specifies that the results must be returned in ascending order, from the lowest to the highest value. This is the default sort type, so results are equivalent to not specifying `ASC`. + +For example: + +```sql +SELECT FirstName, LastName +FROM Sales.SalesPerson +ORDER BY LastName ASC +``` + +| FirstName | LastName | +| ----- | ----- | +| John | Doe | +| Amelia | Doe | +| Oliver | Doe | +| Oliver | Moose | +| Jane | Moose | + +### DESC + +`DESC` specifies that the results must be returned in descending order, from the highest to the lowest value. + +For example: + +```sql +SELECT FirstName, LastName +FROM Sales.SalesPerson +ORDER BY LastName DESC +``` + +| FirstName | LastName | +| ----- | ----- | +| Oliver | Moose | +| Jane | Moose | +| John | Doe | +| Amelia | Doe | +| Oliver | Doe | + +### Multiple `ORDER BY` Criteria + +Multiple criteria can be specified in `ORDER BY`. Separate each criterion with a comma. + +For example: + +```sql +SELECT FirstName, LastName +FROM Sales.SalesPerson +ORDER BY LastName, FirstName +``` + +| FirstName | LastName | +| ----- | ----- | +| Amelia | Doe | +| John | Doe | +| Oliver | Doe | +| Jane | Moose | +| Oliver | Moose | + +You can apply `ASC` and `DESC` modifiers to each criterion separately: + +```sql +SELECT FirstName, LastName +FROM Sales.SalesPerson +ORDER BY LastName DESC, FirstName ASC +``` + +| FirstName | LastName | +| ----- | ----- | +| Jane | Moose | +| Oliver | Moose | +| Amelia | Doe | +| John | Doe | +| Oliver | Doe | + +### `ORDER BY` Associated Attribute + +OQL allows you to specify paths to attributes of associated entities in the `ORDER BY` clause. + +{{% alert color="info" %}} +In the example below `Sales.Customer` object for Jim Elk does not have any associated objects of the `Sales.Request` entity. Depending on the database, that object will end up either at the beginning or the end of the ordered result. See [NULL Values Order Behavior](/refguide/ordering-behavior/#null-ordering-behavior) for more information. +{{% /alert %}} + +```sql +SELECT LastName +FROM Sales.Customer +ORDER BY Sales.Customer/Sales.Customer_Request/Sales.Request/Number +``` + +| LastName | +| ------------ | +| Doe | +| Moose | +| Elk | + +## `LIMIT` and `OFFSET` Clauses {#limit-offset} + +With the `LIMIT` and `OFFSET` clauses, you can specify that only a portion of the results of a query is returned. + +{{% alert color="info" %}} +Mendix recommends combining `LIMIT` and `OFFSET` clauses with `ORDER BY` because the return order of rows without an `ORDER BY` clause is undefined and can lead to unpredictable output. +{{% /alert %}} + +### Syntax + +The syntax is as follows: + +```sql +[ LIMIT number ] [ OFFSET number ] +``` + +### `LIMIT` Clause + +`LIMIT` specifies the maximum amount of rows to return. + +For example, the following query retrieves the first three records sorted by last name and first name: + +```sql +SELECT Brand, City, LocationNumber +FROM Sales.Location +ORDER BY LocationNumber +LIMIT 3 +``` + +| Brand | City | LocationNumber | +| ------ | ----- | ----- | +| Cinco | Rotterdam | 1 | +| Veidt | Rotterdam | 2 | +| Rekall | Zwolle | 3 | + +### `OFFSET` Clause + +`OFFSET` specifies how many rows must be skipped before returning the result rows. + +For example, the following query retrieves all records except the first two: + +```sql +SELECT Brand, City, LocationNumber +FROM Sales.Location +ORDER BY LocationNumber +OFFSET 2 +``` + +| Brand | City | LocationNumber | +| ------ | ----- | ----- | +| Rekall | Zwolle | 3 | +| Rekall | Utrecht | 4 | +| Veidt | Utrecht | 5 | +| Veidt | Rotterdam | 6 | + +`LIMIT` and `OFFSET` can be combined in one query. + +For example, the following query skips 2 records and then returns 3 records: + +```sql +SELECT Brand, City, LocationNumber +FROM Sales.Location +ORDER BY LocationNumber +LIMIT 3 +OFFSET 2 +``` + +| Brand | City | LocationNumber | +| ------ | ----- | ----- | +| Rekall | Zwolle | 3 | +| Rekall | Utrecht | 4 | +| Veidt | Utrecht | 5 | + +## `UNION` Clause {#oql-union} + +The clause takes multiple select queries and combines their results into a single result set. +The resulting set by default only includes distinct rows. The `ALL` keyword can be used to include all rows. Rows are considered distinct if the combination of the column values is distinct from all other rows. Comparison logic of values is the same as the `DISTINCT` keyword of a `SELECT` clause. + +All select queries must define the same number of columns in the same order and their datatypes should match. The resulting column names will be those used in the very first `SELECT` clause. + +### Syntax + +The syntax is as follows: + +```sql + select_query + { + UNION [ALL] select_query + } [ ,...n ] + [ order_by_clause ] + [ LIMIT number ] + [ OFFSET number ] +``` + +{{% todo %}} Document data type behavior when corresponding oql v2 issue is finished {{% /todo %}} + +### Examples + +The following query combines sales person and customer names into a single table: + +```sql +SELECT FirstName, LastName +FROM Sales.SalesPerson +UNION +SELECT FirstName, LastName +FROM Sales.Customer +``` + +| FirstName | LastName | +|-----------|----------| +| John | Doe | +| Amelia | Doe | +| Oliver | Doe | +| Oliver | Moose | +| Jane | Moose | +| Jane | Doe | + +Some names are duplicated across the tables, so only some entries are included in the result. The query below uses `UNION ALL` to include all names the result: + +```sql +SELECT FirstName, LastName +FROM Sales.SalesPerson +UNION ALL +SELECT FirstName, LastName +FROM Sales.Customer +``` + +| FirstName | LastName | +|-----------|----------| +| John | Doe | +| Amelia | Doe | +| Oliver | Doe | +| Oliver | Moose | +| Jane | Moose | +| John | Doe | +| Jane | Doe | +| Jane | Doe | +| Jane | Moose | + +The following query performs a self union of a table, returning fewer rows than the original table, as only distinct rows are included in the result. + +```sql +SELECT FirstName FName, LastName LName +FROM Sales.Customer +UNION +SELECT FirstName FName, LastName LName +FROM Sales.Customer +``` + +| FName | LName | +| ----- | ----- | +| John | Doe | +| Jane | Doe | +| Jane | Moose | + +The result of the union can also be sorted and limited, similar to a normal `SELECT` clause. This query retrieves the first 4 names sorted on both first and last name: + +```sql +SELECT FirstName, LastName +FROM Sales.SalesPerson +UNION +SELECT FirstName, LastName +FROM Sales.Customer +ORDER BY FirstName, LastName +LIMIT 4 +``` + +| FirstName | LastName | +|-----------|----------| +| Amelia | Doe | +| Jane | Doe | +| Jane | Moose | +| John | Doe | + +`UNION` can be chained to use more than 2 select queries as a source. This query collects all distinct first and last names into a single column in a single table: + +```sql +SELECT FirstName AS Name FROM Sales.SalesPerson +UNION +SELECT FirstName AS Name FROM Sales.Customer +UNION +SELECT LastName AS Name FROM Sales.SalesPerson +UNION +SELECT LastName AS Name FROM Sales.Customer +``` + +| Name | +|--------| +| John | +| Amelia | +| Oliver | +| Jane | +| Doe | +| Moose | + +#### Union of associations + +Performing a `UNION` with columns that are associations is possible, given the columns refer to the same entity for all select clauses. + +Presume an entity that has one-to-many association to `Sales.Customer`: + +```sql +SELECT * +FROM Sales.ExtraInfo +``` + +| ID | CustomerName | +|-----------------|--------------| +| 403600446162337 | Doe | +| 403600446169432 | Elk | + +The query below combines associations to the `Sales.Customer` entity from 2 different source entities with duplicates: + +```sql +SELECT Cust.LastName as CustomerName FROM ( + SELECT Sales.Request/Sales.Request_Customer as RequestAssoc + FROM Sales.Request + UNION ALL + SELECT Sales.ExtraInfo/Sales.ExtraInfo_Customer as RequestAssoc + FROM Sales.ExtraInfo +) AS Cust +``` + +| CustomerName | +|--------------| +| Doe | +| Moose | +| Doe | +| Elk | + +## Subqueries + +A subquery is an OQL query nested inside another query. A subquery can contain the same clauses as a regular OQL query. The entities from the outer query can be referred to in a subquery. A subquery can be used in different parts of the query. + +### Subquery in `SELECT` {#subquery-in-select} + + A subquery can be used as a column in the `SELECT` clause. It can refer to other tables and expressions in `FROM`. + +```sql +SELECT + Req/Number AS RequestNumber, + ( + SELECT COUNT(*) + FROM Sales.Customer AS Cust + WHERE Cust/LastName = Req/CustomerName + ) AS CustomerCount +FROM Sales.Request Req +``` + +| RequestNumber | CustomerCount | +| ------------- | ------------- | +| 1 | 1 | +| 2 | 1 | +| -1 | 0 | + +{{% alert color="info" %}} +If you use a subquery as an expression in `SELECT`, then such subquery should always return a single row and column. If the subquery returns more than one row or column, that will lead to an exception during runtime. +{{% /alert %}} + +### Subquery in `FROM` {#subquery-in-from} + +It is possible to use a subquery in `FROM`. For example: + +```sql +SELECT Cust/LastName +FROM ( + SELECT * + FROM Sales.Customer + ) AS Cust +``` + +| LastName | +| -------- | +| Doe | +| Moose | +| Elk | + +Subqueries in `FROM` can be combined with other tables: + +```sql +SELECT Cust/LastName, Req/Number +FROM + Sales.Request AS Req, + ( + SELECT * + FROM Sales.Customer + ) AS Cust +WHERE + Req.CustomerName = Cust.LastName +``` + +| LastName | Number | +| -------- | ------ | +| Doe | 1 | +| Moose | 2 | + +It is possible to refer to other tables in the outer `FROM` clause from a subquery: + +```sql +SELECT Cust/LastName, Req/MaxNumber +FROM + Sales.Customer AS Cust, + ( + SELECT MAX(Number) as MaxNumber + FROM Sales.Request + WHERE Req.CustomerName = Cust.LastName + ) AS Req +``` + +| LastName | MaxNumber | +| -------- | --------- | +| Doe | 1 | +| Moose | 2 | + +`JOIN` is also supported: + +```sql +SELECT Cust/LastName, Req/Number +FROM + Sales.Request Req + LEFT JOIN + ( + SELECT * + FROM Sales.Customer + ) AS Cust + ON Req.CustomerName = Cust.LastName +``` + +| LastName | Number | +| -------- | ------ | +| Doe | 1 | +| Moose | 2 | +| NULL | -1 | + +### Subquery in `WHERE` {#subquery-in-where} + +A subquery can be used in the `WHERE` clause. There are multiple ways to use subqueries + +#### Subquery as a Value + +An outcome of a subquery can be used as a value to be compared to another value or expression. In this case, the subquery should always return exactly one column and at most one row. + +For example: + +```sql +SELECT Brand, City +FROM Sales.Location AS Location +WHERE + Location.Stock = ( + SELECT MAX(Stock) + FROM Sales.Location AS MaxStockLocation + WHERE Location.City = MaxStockLocation.City + ) +``` + +| Brand | City | +| ------ | ----- | +| Rekall | Utrecht | +| Rekall | Zwolle | +| Veidt | Rotterdam | + +#### Subquery with `IN` + +A subquery can be combined with the `IN` keyword. In that case, the expression is true if a value in the outer query matches one of the results of the subquery. In this case, the subquery can return any number of rows, but it should always return exactly one column. + +```sql +SELECT FirstName, LastName +FROM Sales.Customer Cust +WHERE + Cust/LastName IN ( + SELECT CustomerName + FROM Sales.Request Req + ) +``` + +| FirstName | LastName | +| --------- | -------- | +| John | Doe | +| Jane | Moose | + +#### Subquery with `EXISTS` + +A subquery can be combined with the `EXISTS` keyword. In that case, the expression is true if the subquery returns at least one row. In case of `EXISTS`, the subquery can return any number of rows and any number of columns. + +```sql +SELECT FirstName, LastName +FROM Sales.Customer Cust +WHERE + EXISTS ( + SELECT * FROM Sales.Request Req + WHERE Req/CustomerName = Cust/LastName + ) +``` + +| FirstName | LastName | +| --------- | -------- | +| John | Doe | +| Jane | Moose | + +### Subquery in `HAVING` {#subquery-in-having} + +Subqueries can be used in a `HAVING` clause the in same way they are used in `WHERE`: as a value or combined with `IN` or `EXISTS` clauses. For other cases, the same limitations apply to the subquery outcome. + +Example: + +```sql +SELECT COUNT(*) AS LocationCount, SUM(Stock) as CityStock, City AS City +FROM Sales.Location AS Location +GROUP BY City +HAVING + SUM(Stock) <= ( + SELECT COUNT(*) + FROM Sales.Location + ) +``` + +| LocationCount | CityStock | City | +| ------------- | --------- | ------ | +| 1 | 3 | Zwolle | + +Example of `EXISTS`: + +```sql +SELECT COUNT(*) AS LocationCount, City AS City +FROM Sales.Location AS Location +GROUP BY City +HAVING + EXISTS ( + SELECT * + FROM Sales.Location AS SubLocation + WHERE + Location/City = SubLocation/City + AND SubLocation/Brand = 'Rekall' + ) +``` + +| LocationCount | City | +| ------------- | --------- | +| 2 | Utrecht | +| 1 | Zwolle | + +The same result can be achieved with `IN`: + +```sql +SELECT COUNT(*) AS LocationCount, City AS City +FROM Sales.Location AS Location +GROUP BY City +HAVING + Location/City IN ( + SELECT SubLocation/City + FROM Sales.Location AS SubLocation + WHERE SubLocation/Brand = 'Rekall' + ) +``` + +| LocationCount | City | +| ------------- | --------- | +| 2 | Utrecht | +| 1 | Zwolle | diff --git a/content/en/docs/refguide/modeling/domain-model/oql/oql-expression-syntax.md b/content/en/docs/refguide/modeling/domain-model/oql/oql-expression-syntax.md new file mode 100644 index 00000000000..cfcfdec902c --- /dev/null +++ b/content/en/docs/refguide/modeling/domain-model/oql/oql-expression-syntax.md @@ -0,0 +1,1218 @@ +--- +title: "OQL Expression Syntax" +url: /refguide/oql-expression-syntax/ +weight: 30 +aliases: + - /refguide/oql-case-expression/ + - /refguide/oql-cast/ + - /refguide/oql-coalesce/ + - /refguide/oql-datediff/ + - /refguide/oql-datepart/ + - /refguide/oql-functions/ + - /refguide/oql-length/ + - /refguide/oql-lower/ + - /refguide/oql-operators/ + - /refguide/oql-rangebegin/ + - /refguide/oql-rangeend/ + - /refguide/oql-replace/ + - /refguide/oql-round/ + - /refguide/oql-upper/ +--- + +## Introduction + +Operators and functions in OQL use expressions as inputs to perform mathematical, comparison, conditional, string, date operations and return the result. They allow an OQL query to perform modifications on data on the database to present a different view of the data or make complex conditions. + +This document details the use and syntax of expressions in an OQL query. + +## Data Types + +OQL supports a set of data types that differ slightly from [Mendix data types](/refguide/data-types/). The supported data types are: + +| Data Type | Mendix Data type | Example | Description | +|------------|------------------|-----------------------|--------------------------------------------| +| `BOOLEAN` | Boolean | `TRUE` | Conditional data, can be `TRUE` or `FALSE` | +| `DATETIME` | Date and time | '2025-07-05 00:00:00' | Date and time data | +| `DECIMAL` | Decimal | 5.3 | Floating point numeric data | +| `INTEGER` | Integer/Long | 5 | Integer data | +| `LONG` | Integer/Long | 5 | 64 bit width integer data | +| `STRING` | String | 'my_string' | Textual data | + +## Literals + +Literals represent values that are constant and are part of the query itself. The supported literals are detailed below: + +| Format | Example | Data Type | Description | +|--------|-----------------|----------------------|-----------------------------------------------| +| | `TRUE`, `FALSE` | `BOOLEAN` | Conditional constants | +| 's*' | 'my_string' | `STRING` | String literal | +| d+ | 5 | `INTEGER` and `LONG` | Natural number literal | +| d+.d+ | 5.3 | `DECIMAL` | Real number literal | +| | `NULL` | N/A | NULL literal to represent non-existent values. Note that OQL does not support keyword `empty`. In XPath, it has identical purpose as `NULL`. | + +Where `d` is a number, `s` is any character, * indicates that the pattern can contain zero or more characters, and + indicates that the pattern can contain one or more characters. + +### DATETIME + +There is no direct support for `DATETIME` literals. For functions that take `DATETIME` as input, it can be represented with a `STRING` in a ISO date time format or a `LONG` value representing Unix seconds. + +## System variables + +Most XPath [system variables](/refguide/xpath-keywords-and-system-variables/#system-variables) can be used in OQL with the format: + +```sql +'[%SystemVariable%]' +``` + +These variables can be used the same way as other expressions. + +### Variables related to entities in System module + +`[%CurrentUser%]` system variable contains an association to the `System.User` object. + +`[%UserRole_%]` variable contains an association to the object of entity `System.UserRole` that corresponds to role ``. + +`[%CurrentObject%]` is not supported in OQL. + +Both `[%CurrentUser%]` and `[%UserRole_%]` can be used only as references. They cannot be cast to other data types. + +For example, this query gets the names of all `Sales.Person` objects that are owned by current user: + +```sql +SELECT + Name +FROM + Sales.Person +WHERE + System.owner = '[%CurrentUser%]' +``` + +This query returns names of all `Sales.Person` objects that are owned by users with role `Manager`: + +```sql +SELECT + Name +FROM + Sales.Person +WHERE + System.owner/System.User/System.UserRoles = '[%UserRole_Manager%]' +``` + +### Time-related variables + +All time-related variables and expressions that are supported in XPath are also supported in OQL. See section [Time-Related](https://docs.mendix.com/refguide/xpath-keywords-and-system-variables/#time-related) in XPath Keywords and System Variables. + +Return type of all time-related variables and expressions is Date and time. They can be used the same way as values of type Date and time. + +For example: + +```sql +SELECT + BirthDate, + DATEPART(YEAR, '[%BeginOfCurrentYear%]') AS CurrentYear, + DATEDIFF(YEAR, BirthDate, '[%CurrentDateTime%]') AS Age, + '[%BeginOfCurrentDay%] - 3 * [%YearLength%]' AS TodayThreeYearsAgo +FROM + Sales.Person +``` + +## Operators + +Operators perform common operations and, unlike functions, do not put their parameters in parentheses. They take `expression` as input, which can be other operator results, functions, columns and literals. + +Supported operators are split into binary, unary, and other operators based on their syntax. +These are further subdivided into logical and arithmetic operators, depending on their return type. Logical operators always return a `BOOLEAN` type. The return type of arithmetic operators depends on the datatypes of the expressions being operated on. `CASE` is detailed separately. + +### Binary operators + +These are the supported binary operators: + +| Operator | Description | Type | +|----------|--------------------------|------------| +| `+` | Addition | Arithmetic | +| `-` | Subtraction | Arithmetic | +| `*` | Multiplication | Arithmetic | +| `:` | Division | Arithmetic | +| `%` | Modulo | Arithmetic | +| `=` | Equal to | Logical | +| `!=` | Not equal to | Logical | +| `<` | Less than | Logical | +| `<=` | Less than or equal to | Logical | +| `>` | Greater than | Logical | +| `>=` | Greater than or equal to | Logical | +| `OR` | Logical disjunction | Logical | +| `AND` | Logical conjunction | Logical | + +Binary operators are used with this syntax: + +```sql + expression operator expression +``` + +Where `operator` is any available binary operator. Both `expression` operands should be of compatible types for the operator and compatible with the other operand. + +#### Type Coercion Precedence {#type-coercion} + +Binary operations perform type casting when operands have different types. For operations involving only numeric types, data types are always upcasted to ensure data types match. The resulting type will be the operand type with the highest precedence according to this ordering: + +* `DECIMAL` +* `LONG` +* `INTEGER` + +{{% alert color="info" %}} +This precedence rule does not apply for operations where at least one of the operands is non-numeric, including that of type `STRING`. In this case, the final result type will depend on the database. +{{% /alert %}} + +#### + (Addition) + +Performs different operations depending on the first `expression` datatype. A numeric input performs a arithmetic addition, while a `STRING` input performs string concatenation. + +Assume `Sales.Customer` contains two objects and `Sales.Order` contains three objects. + +```sql +SELECT * FROM Sales.Customer +``` + +| ID | LastName | FirstName | +|----|----------|-----------| +| - | Doe | John | +| - | Moose | Jane | + +```sql +SELECT * FROM Sales.Order +``` + +| ID | LastName | Number | Price | +|----|:---------|-------:|------:| +| - | Doe | 7 | 1.5 | +| - | Doe | 2 | 5.0 | +| - | Moose | 3 | 8.2 | + +The operator can be used to modify an attribute in SELECT. + +```sql +SELECT LastName, (Number + 5) AS N FROM Sales.Order +``` + +| LastName | N | +|----------|-------:| +| Doe | 12 | +| Doe | 8 | +| Moose | 7 | + +It can also be used for complex `WHERE` comparisons. The following query checks for equality of the full name of a customer: + +```sql +SELECT LastName FROM Sales.Customer WHERE (FirstName + LastName) = 'JaneMoose' +``` + +| LastName | +|----------| +| Moose | + +#### - (Subtraction) + +Subtracts the right `expression` from the left one. Both operands must be numeric. + +Assume `Sales.Finances` contains two objects: + +```sql +SELECT * FROM Sales.Finances +``` + +| ID | Revenue | Cost | +|:---|--------:|-----:| +| - | 10 | 7 | +| - | NULL | 10 | + +We can calculate a profit based on this data: + +```sql + Select (Revenue - Cost) as Profit FROM Sales.Finances + ``` + +| Profit | +|--------| +| 3 | +| NULL | + +#### * (Multiplication) + +Multiplies expressions. + +For example, it can be used to get the total value of an order: + +```sql +SELECT LastName, (Number * Price) as Total FROM Sales.Order +``` + +| LastName | Total | +|----------|------:| +| Doe | 10.5 | +| Doe | 10.0 | +| Moose | 24.6 | + +#### : (Division) + +Divides left `expression` by the right `expression`. Supports long, integer, and decimal division. In case of long and integer division, the remainder is discarded. + +#### % (Modulo) + +Returns the remainder of a division. The behavior is database dependent when one of the `expression` is of type `DECIMAL`. + +{{% alert color="info" %}} +The operator throws an error in PostgresSQL and SQL Server when one of the operands is a parameter of type `DECIMAL` +{{% /alert %}} + +#### = (Equal To) + +Returns `TRUE` if both `expression` inputs are equal. When used with `NULL`, it will always return a `FALSE` result. To compare to `NULL` values, use the [IS](#is-operator) operator. + +{{% alert color="info" %}} +Note that `DECIMAL` values have to match exactly. Use [`ROUND`](#round) to compare with less precision. +{{% /alert %}} + +The = operator is useful for checking exact matches in data. For example, this query retrieves a specific customer's orders: + +```sql +SELECT LastName, Number FROM Sales.Order WHERE LastName = Moose +``` + +| LastName | Number | +|----------|-------:| +| Moose | 12 | + +#### != (Not Equal To) + +Inverse of `=`. The same `NULL` handling rules apply. Partial expression `expression !=` is equivalent to `NOT expression =`. + +#### < (Less Than) + +Returns `TRUE` if the left `expression` is less than the right. Both `expression` must be numeric. + +It can be used for filtering data with the use of a `WHERE` clause. For example: + +```sql +SELECT LastName, Number, Price FROM Sales.Order WHERE Price < 5 +``` + +| LastName | Number | Price | +|----------|-------:|------:| +| Doe | 7 | 1.5 | + +#### <= (Less Than Or Equal To) + +Returns `TRUE` if the left `expression` is less than or equal to the right. Both `expression` must be numeric. + +#### \> (Greater Than) + +Returns `TRUE` if the left `expression` is greater than the right. Both `expression` must be numeric. + +#### \>= (Greater Than Or Equal To) + +Returns `TRUE` is the left `expression` is greater than or equal to the right. Both `expression` must be numeric. + +#### OR + +Returns `TRUE` if at least one input `expression` returns `TRUE`. Both `expression` must be of type `BOOLEAN`. + +#### AND + +Returns `TRUE` if both input `expression` return `TRUE`. Both `expression` must be of type `BOOLEAN`. + +Its main use is to make complex `WHERE` conditions with a combination of input values. + +For example, in the following query, large orders or smaller orders with a high value are selected: + +```sql +SELECT LastName, Number, Price FROM Sales.Order WHERE Number >= 5 OR Price > 4 AND Number >= 3 +``` + +| LastName | Number | Price | +|----------|-------:|------:| +| Doe | 7 | 1.5 | +| Moose | 3 | 8.2 | + +Note that in the query above `AND` is evaluated first. The following query with parentheses returns orders that have low volume or low price with a minimum of 3 orders: + +```sql +SELECT LastName, Number, Price FROM Sales.Order WHERE (Number <= 5 OR Price < 6) AND Number >= 3 +``` + +| LastName | Number | Price | +|:---------|-------:|------:| +| Doe | 7 | 1.5 | + +### Unary Operators + +Unary operators only have a single argument. The following unary operators are supported: + +| Operator | Description | type | +|----------|---------------------|------------| +| `-` | Arithmetic negation | Arithmetic | +| `NOT` | Logical negation | Logical | + +Unary operators are used with the following syntax: + +```sql + operator expression +``` + +`expression` should be of a type compatible with the `operator`. + +#### - (Arithmetic Negation) + +Negates a numeric value. The return type is the same as the input `expression`. + +#### NOT + +Reverses Boolean `TRUE` values into `FALSE` and vice versa. + +### Other operators {#other-operators} + +The operators in this section do not match the general unary or binary syntax. They are all logical operators: + +| Operator | Description | +|----------|-----------------------------------------------------------------| +| `LIKE` | Matches a string to a specified pattern | +| `IN` | Matches any value in a subquery or a list of expression values. | +| `EXISTS` | Test for the existence of any rows when executing the subquery. | +| `IS` | Tests if a value is `NULL` | + +#### LIKE + +Matches an `expression` to the pattern after the operator. + +##### Syntax + +The syntax of the `LIKE` operator is as follows: + +```sql +expression LIKE pattern +``` + +Where `expression` is of type `STRING` and `pattern` is a string literal or parameter. Note that this means functions are not allowed to be used in `pattern`. A `NULL` pattern is treated as an empty string. + +The pattern can have special characters, which are all wildcards. The following wildcard characters are supported: + +| Wildcard Character | Description | +|--------------------|---------------------------------------| +| `%` | Matches zero or more of any character | +| `_` | Matches one of any character | + +In order to search for special characters, they should be escaped with the `\` escape character (including `\` itself). + +##### Examples + +For example, say we have 3 strings for column `PropertyType`: `Apartment`, `Tenement`, and `Flat`. We can select all strings ending with "ment" with this condition: + +```sql +Select PropertyType FROM RealEstate.Properties WHERE PropertyType LIKE '%ment' +``` + +| PropertyType | +|-----------| +| Apartment | +| Tenement | + +A certain length of string can be enforced with the use of the `_` operator This query matches any string that has 4 of any character ending with "ment": + +```sql +Select PropertyType FROM RealEstate.Properties WHERE PropertyType LIKE '____ment' +``` + +| PropertyType | +|-----------| +| Tenement | + +This query will match any string containing the letter "a" and ending in "t": + +```sql +Select PropertyType FROM RealEstate.Properties WHERE PropertyType LIKE '%a%t' +``` + +| PropertyType | +|-----------| +| Apartment | +| Flat | + +#### IN + +Matches a value in a subquery or a list of expression values. Each value in the list or subquery is compared to a specified expression with the operator `=`(Equal to), returning `TRUE` if any of the comparisons return `TRUE`. `NULL` value handling is the same as the `=`(Equal to) operator. + +{{% alert color="info" %}} +HSQLDB and PostgreSQL do not support matching of different datatypes. +{{% /alert %}} + +##### Syntax + +The syntax of the `IN` operator is as follows: + +```sql +expression IN { + subquery + | ( expression [ ,...n] ) + | parameter +} +``` + +Where `expression` can have any type. The left side can be either a `subquery`, a comma separated list of `expression`, or a parameter that is a list of values. If `subquery` is used, it must return a single column. + +##### Examples + +The `IN` operator is used to create conditions that depend on other entities or limited views of entities. + +For example, the condition below checks if the string `House` is in the literal list on the right, and returns `FALSE`: + +```sql +'House' IN ('Apartment','Shed','Shack') +``` + +This query retrieves all customers that have an order larger than 3: + +```sql +SELECT LastName, FirstName +FROM Sales.Customer +WHERE LastName IN + (SELECT subq.LastName + FROM Sales.Order subq + WHERE subq.Number > 3) +``` + +| LastName | FirstName | +|----------|-----------| +| Doe | John | + +#### EXISTS + +Returns `TRUE` if a `subquery` returns at least one row. + +##### Syntax + +The syntax of the `EXISTS` operator is as follows: + +```sql +EXISTS subquery +``` + +Where `subquery` is any query. + +##### Examples + +The `EXISTS` operator can be used to check if an entity contains any object matching a condition. + +For example, the following condition: + +```sql +EXISTS (SELECT * FROM Sales.Customer WHERE LastName = 'Mose') +``` + +returns `FALSE` as there are no customers with the last name `Mose`. + +This query returns all customers that also have orders placed: + +```sql +SELECT * +FROM Sales.Customer customer +WHERE EXISTS + (SELECT * + FROM Sales.Order order + WHERE order.LastName = customer.LastName) +``` + +| ID | LastName | FirstName | +|----|----------|-----------| +| - | Doe | John | +| - | Moose | Jane | + +#### IS {#is-operator} + +Tests for an expression being `NULL`. Can be inverted with an optional `NOT`. + +##### Syntax + +The syntax of the `IS` operator is as follows: + +```sql +expression IS [ NOT ] NULL +``` + +Where `expression` is an expression of any datatype. + +##### Examples + +The `IS` operator can be used to filter out rows with values that are NULL. For example: + +```sql + SELECT Revenue, Cost FROM Sales.Finance WHERE Revenue IS NOT NULL +``` + +| Revenue | Cost | +|--------:|-----:| +| 10 | 7 | + +### CASE {#case-expression} + +The `CASE` expression is a conditional expression, similar to if/else statements in other programming languages. If the result of a following `WHEN` condition is `TRUE`, the value of the `CASE` expression is the result that follows the condition and the remainder of the `CASE` expression is not processed. If the result is not `TRUE`, any subsequent `WHEN` clauses are examined in the same manner. If no `WHEN` condition yields `TRUE`, the value of the `CASE` expression is the result of the `ELSE` clause. If the `ELSE` clause is omitted and no condition is `TRUE`, the result is null. + +If [OQL v2](/refguide/oql-v2/) is enabled, additional data type validations apply to result expressions of `CASE`. See the corresponding [page](/refguide/oql-v2/#case-validations) for details. + +#### Syntax + +The `CASE` expression can be used in two ways – simple: + +```sql + CASE input_expression + { WHEN when_expression THEN result_expression } [ ...n ] + ELSE else_result_expression + END +``` + +In a simple `CASE` expression, `input_expression` will be compared to `when_expression`. If `input_expression` matches `when_expression`, the result of the whole `CASE` expression will be `result_expression` given after `THEN`. The data types of `input_expression` and `when_expression` must tch. + +There is also an extended version: + +```sql + CASE + { WHEN boolean_expression THEN result_expression } [ ...n ] + ELSE else_result_expression + END +``` + +In an extended Case expression, `boolean_expression` is evaluated and if it is `TRUE`, the result of the whole `CASE` expression will be `result_expression` given after `THEN`. `boolean_expression` must have return type `BOOLEAN`. + +In both instances, `else_result_expression` is the result of the whole `CASE` expression, when no previous `when_expression` matched or no previous `boolean_expression` returned `TRUE`. + +#### Examples {#case-expression-examples} + +Simple expression: + +```sql +SELECT + LastName, + Number, + CASE Number + WHEN 7 THEN True + ELSE False + END AS IsLuckyNumber +FROM Sales.Order +``` + +| LastName | Number | IsLuckyNumber | +|:---------|-------:|:--------------| +| Doe | 7 | True | +| Doe | 2 | False | +| Moose | 3 | False | + +Extended expression: + +```sql +SELECT + LastName, + Number, + Price, + CASE + WHEN Price > 7 THEN 'Priority' + WHEN Number = 7 THEN 'Lucky' + ELSE 'Regular' + END AS OrderType +FROM Sales.Order +``` + +| LastName | Number | Price | OrderType | +|:---------|-------:|------:|:----------| +| Doe | 7 | 1.5 | Lucky | +| Doe | 2 | 5.0 | Regular | +| Moose | 3 | 8.2 | Priority | + +If result expressions have different numeric types, date type of the result expression in the first WHEN has priority, and the whole CASE expression has type of that result expression. This behavior matches the behavior of supported database vendors. + +```sql +SELECT + LastName, + Number, + Price, + CASE Name + WHEN 'Doe' THEN Price + ELSE Number + END AS PriceOrNumber, + CASE Name + WHEN 'Doe' THEN Number + ELSE Price + END AS NumberOrPrice +FROM Sales.Order +``` + +| LastName | Number | Price | PriceOrNumber (type: Decimal) | NumberOrPrice (type: Integer) | +|:---------|-------:|------:|--------------:|--------------:| +| Doe | 7 | 1.5 | 1.5 | 7 | +| Doe | 2 | 5.0 | 5.0 | 2 | +| Moose | 3 | 8.2 | 3.0 | 8 | + +### Operator Precedence + +If operators are used without parenthesis to indicate order, the order of application is left to right with operator precedence: + +* \* (Multiplication), : (Division), % (Modulo) +* \- (Arithmetic negation), + (Addition), - (Subtraction) +* =, >, <, >=, <=, !=, IS, IN, EXISTS, LIKE +* NOT +* AND +* OR + +### NULL Handling + +If one of the `expression` in a binary operation or the unary `expression` have a `NULL` value, then the return type will also be NULL. + +This does not apply to the `=` and `!=` operators. Handling of `NULL` in [other operators](#other-operators) is detailed in the specific operator subsections. + +## String Coercion + +In some databases, using `STRING` type variables in place of numeric, `DATETIME` or `BOOLEAN` values in operators and functions that explicitly require those types, causes the database to perform an implicit conversion. A common example would be the use of a `STRING` representation of a `DATETIME` variable inside a `DATEPART` function. Mendix recommends that you always [cast](#cast) strings to the exact type the operator or functions. + +## Functions + +These are the currently supported functions: + +* CAST +* COALESCE +* DATEDIFF +* DATEPART +* LENGTH +* LOWER +* RANGEBEGIN +* RANGEEND +* REPLACE +* ROUND +* UPPER + +### CAST{#cast} + +The `CAST` function converts an expression to a specified data type. + +#### Syntax + +The syntax is as follows: + +```sql +CAST ( expression AS data_type ) +``` + +##### expression + +`expression` specifies the expression to convert. + +##### data_type + +`data_type` specifies the data type to convert the expression to. The data type can be one of the following: + +* `BOOLEAN` +* `DATETIME` +* `DECIMAL` +* `INTEGER` +* `LONG` +* `STRING` + +#### Supported Conversions + +The table below describes which `CAST` conversions are supported: + +* ✔ – the conversion is supported +* ✔* – the conversion is supported, but the behavior differs per database +* ✘ – the conversion is not supported + +| From \ To | BOOLEAN | DATETIME | DECIMAL | INTEGER | LONG | STRING (unlimited) | STRING (limited) | +|------| :------: | :------: | :------: | :------: | :------: | :------: | :------: | +| BOOLEAN | ✔ | ✘ | ✘ | ✘ | ✘ | ✔* | ✔*¹ | +| DATETIME | ✘ | ✔ | ✘ | ✘ | ✘ | ✔* | ✔*² | +| DECIMAL | ✘ | ✘ | ✔* | ✔* | ✔* | ✔* | ✔*² | +| INTEGER | ✘ | ✘ | ✔ | ✔ | ✔ | ✔ | ✔ | +| LONG | ✘ | ✘ | ✔ | ✔ | ✔ | ✔ | ✔ | +| STRING | ✘ | ✘ | ✔ | ✔ | ✔ | ✔ | ✔ | + +¹BOOLEAN to STRING (limited) is supported only if the resulting string length is greater than or equal to 5. + +²The conversion of DATETIME and DECIMAL to STRING (limited) is supported only if the value fully fits into the string length. The conversion can fail if the resulting string length is less than 20. + +Converting `DATETIME` or `BOOLEAN` to `STRING` returns different format per database. + +#### Examples + +A frequent use case for `CAST` is to convert your date from the `DATETIME` data type to a text formatted `STRING` type: + +```sql +CAST ( datetime_column AS STRING ) +``` + +Explicit conversions can also be useful for numeric data types, like ensuring a division operation is a floating point division and the remainder is not discarded: + +```sql +SELECT (Number : 2) as Normal, (Cast(Number AS DECIMAL) : 2) as Casted FROM Sales.Order Where Number = 7 +``` + +| Normal | Casted | +|------:|-------:| +| 3 | 3.5 | +| 1 | 1.0 | +| 1 | 1.5 | + +### COALESCE {#coalesce-expression} + +Returns the value of the first `expression` that is not NULL. Can be used with columns. + +If [OQL v2](/refguide/oql-v2/) is enabled, additional data type validations apply to arguments of `COALESCE`. See the corresponding [page](/refguide/oql-v2/#coalesce-validations) for details. + +#### Syntax + +The syntax is as follows: + +```sql +COALESCE ( expression [ ,...n ] ) +``` + +`expression` specifies the expression to check. Most databases expect the function to be given at least two `expression` arguments. + +#### Examples {#coalesce-expression-examples} + +Assume entity `Sales.Customer` entity now has some `NULL` values: + +```sql +SELECT * FROM Sales.Customer +``` + +| ID | LastName | FirstName | Age | TotalOrderAmount | +|----|----------|-----------|-----:|-----:| +| - | Doe | NULL | 25 | NULL | +| - | NULL | Jane | NULL | 42.3 | + +Selecting a non-null name for a customer, ignoring if it is the first name or last name, can be done with `COALESCE`: + +```sql +SELECT COALESCE(LastName, FirstName) AS Name FROM Sales.Customer +``` + +| Name | +|------| +| Doe | +| Jane | + +If arguments of `COALESCE` have different numeric types, the expression gets the type of the first argument. This behavior matches the behavior of supported database vendors. + +```sql +SELECT + COALESCE(Age, TotalOrderAmount) AS AgeOrAmount, + COALESCE(TotalOrderAmount, Age) AS AmountOrAge, +FROM Sales.Customer +``` + +| AgeOrAmount (type: Integer) | AmountOrAge (type: Decimal) | +|------:|------:| +| 25 | 25.0 | +| 42 | 42.3 | + +### DATEDIFF {#datediff-function} + +The `DATEDIFF` function returns the difference between two given `DATETIME` expressions. The difference is given in the specified unit. + +If [OQL v2](/refguide/oql-v2/) is enabled, additional data type validations apply to the arguments of `DATEDIFF`. See the corresponding [page](/refguide/oql-v2/#date-validations) for details. + +#### Syntax + +The syntax is as follows: + +```sql +DATEDIFF ( unit , startdate_expression, enddate_expression [, timezone ] ) +``` + +##### unit + +`unit` specifies the unit of the `DATETIME` value to retrieve. This can be one of the following: + +* `YEAR`, +* `QUARTER`, +* `MONTH`, +* `DAY`, +* `WEEK`, +* `HOUR`, +* `MINUTE`, +* `SECOND` +* `MILLISECOND`. + +For more information on `DATETIME` values, see the [example section under *DATEPART*](#oql-datepart-example), below. + +##### startdate_expression + +`startdate_expression` specifies the start date of the period being calculated. The expression should resolve to a `DATETIME` value. String representations of `DATETIME` are accepted. + +##### enddate_expression + +`enddate_expression` specifies the end date of the period being calculated. The expression should resolve to a `DATETIME` value. String representations of `DATETIME` are accepted. + +##### timezone + +`timezone` specifies the time zone to use for the retrieval. This parameter is optional and defaults to the local time zone. It should be a string literal containing an [IANA time zone](https://www.iana.org/time-zones). GMT offset time zones are not supported. + +#### Examples + +Assume the entity `Sales.Period` has 2 objects: + +```sql +SELECT * FROM Sales.Period +``` + +| ID | Start | End | Revenue | +|:---|---------------------|---------------------|--------:| +| - | 2024-05-02 00:00:00 | 2025-07-05 00:00:00 | 28 | +| - | 2024-05-02 00:00:00 | 2024-06-02 15:12:45 | 10 | + +You can use `DATEDIFF` to get the time interval between two dates: + +```sql +SELECT DATEDIFF(MONTH , End, Start ) as difference FROM Sales.Period +``` + +| difference | +|-----------:| +| 14 | +| 1 | + +This interval can be used to calculate the average revenue per month: + +```sql +SELECT Revenue : DATEDIFF(MONTH, End, Start ) as avg_revenue FROM Sales.Period +``` + +| avg_revenue | +|------------:| +| 2 | +| 10 | + +{{% alert color="info" %}} +The way the difference is calculated depends on the database. The `YEAR` difference between "2002-01-01" and "2001-12-31" will be `1` with some databases and `0` with others. +{{% /alert %}} + +### DATEPART {#datepart-function} + +The `DATEPART` function retrieves a specified element from `DATETIME` values. The return type is `INTEGER`. + +If [OQL v2](/refguide/oql-v2/) is enabled, additional data type validations apply to the arguments of `DATEPART`. See the corresponding [page](/refguide/oql-v2/#date-validations) for details. + +#### Syntax + +The syntax is as follows: + +```sql +DATEPART ( datepart , date_expression [, timezone ] ) +``` + +##### datepart + +`datepart` specifies the part of the `DATETIME` value to retrieve. For possible values, see the [Example](#oql-datepart-example) below. + +##### date_expression + +`date_expression` specifies the date to retrieve an element from. The expression should resolve to a `DATETIME` value, string representations of `DATETIME` are accepted. + +##### timezone + +`timezone` specifies the time zone to use for the retrieval. This parameter is optional and defaults to the local time zone. It should be a string literal containing an IANA time zone. GMT offset time zones are not supported. + +#### Examples{#oql-datepart-example} + +| datepart | Definition | Example (Friday July 1, 2005, 16:34:20.356) | +|--------------|--------------------------------------------------|---------------------------------------------| +| `YEAR` | | 2005 | +| `QUARTER` | 1, 2, 3 or 4 | 3 | +| `MONTH` | 1 to 12 | 7 | +| `DAYOFYEAR` | 1 to 366 | 182 | +| `DAY` | 1 to 31 | 5 | +| `WEEK` | 1 to 53 (depends on the database implementation) | 26 (using defaults for the US) | +| `WEEKDAY` | 1 to 7 (1 = Sunday, 7 = Saturday) | 6 | +| `HOUR` | 0 to 23 | 16 | +| `MINUTE` | 0 to 59 | 34 | +| `SECOND` | 0 to 59 | 20 | +| `MILLISECOND` | 0 to 999 | 356 | + +`DATEPART` can be used to filter dates on specific components. The following query returns all end dates that are in the year "2025". + +```sql +SELECT End FROM Sales.Period WHERE DATEPART(YEAR, End) = 2025 +``` + +| End | +|---------------------| +| 2025-07-05 00:00:00 | + +### LENGTH {#length-function} + +#### Description + +The `LENGTH` function returns the length in characters of the result of a string expression. + +If [OQL v2](/refguide/oql-v2/) is enabled, additional data type validations apply. See the corresponding [page](/refguide/oql-v2/#length-validations) for details. + +#### Syntax + +The syntax is as follows: + +```sql +LENGTH ( expression ) +``` + +Where `expression` is an expression of type `STRING`. + +#### Example + +The function is used to get the length of strings and can be used for miscellaneous purposes like statistics. Assume we have an entity `Sales.Reports` that contains a field with long text: + +```sql +SELECT * FROM Sales.Reports +``` + +| ID | Text | +|----|-------------------------------| +| - | "Performance is satisfactory" | +| - | "Order has been completed" | + +You can return an extra column containing the calculated length of the Text as follows: + +```sql +SELECT Text, LENGTH(Text) as text_length FROM Sales.Reports +``` + +| Text | text_length | +|-------------------------------|------------:| +| "Performance is satisfactory" | 27 | +| "Order has been completed" | 24 | + +### LOWER{#lower-function} + +#### Description + +Converts all uppercase characters in a given string to lowercase. + +#### Syntax + +The syntax is as follows: + +```sql +LOWER ( expression ) +``` + +`expression` specifies the string to convert. + +#### Example + +The function is useful to enforce consistent case for all strings, especially for comparisons. + +For example, the following query would return no results in case-sensitive databases, as there is only a "Doe": + +```sql +SELECT * FROM Sales.Customer WHERE LastName = 'doe' +``` + +Using `LOWER` this inconsistency can be fully avoided: + +```sql +SELECT * FROM Sales.Customer WHERE LOWER(LastName) = 'doe' +``` + +| ID | LastName | FirstName | +|----|----------|-----------| +| - | Doe | John | + +{{% alert color="info" %}} +This query can no longer take advantage of an index for `LastName` for comparison, resulting in a performance decrease. +{{% /alert %}} + +### Ranges in Datasets + +{{% alert color="info" %}} +Range parameters are defined only in [datasets](/refguide/data-sets/). +{{% /alert %}} + +`RANGEBEGIN` and `RANGEEND` can only be used with a [parameter](/refguide/oql-expressions-new/#parameters) as input. + +#### RANGEBEGIN + +Extracts the initial value of a range parameter. + +##### Syntax + +```sql +RANGEBEGIN ( $range ) +``` + +`$range` specifies the range parameter. + +##### Example(#oql-rangebegin-example) + +Assume `$now` is "2024-06-15 00:00:00" and there are 3 range parameters defined in a dataset: + +* `$range` with start value "2024-06-01 00:00:00" and end value "2025-06-01 00:00:00" +* `$range_future` with start value `$now` +* `$range_past` with end value `$now` + +| ID | Start | End | Revenue | +|:---|---------------------|---------------------|--------:| +| - | 2024-05-02 00:00:00 | 2025-07-05 00:00:00 | 28 | +| - | 2024-05-02 00:00:00 | 2024-06-02 15:12:45 | 10 | + +This query uses `$range_future` to retrieve all periods that end in the future: + +```sql +SELECT End, Revenue FROM Sales.Period +WHERE End > RANGEBEGIN($range_future) +``` + +| End | Revenue | +|---------------------|--------:| +| 2025-07-05 00:00:00 | 28 | + +#### RANGEEND{#oql-rangeend} + +Extracts the end value of a range parameter. + +##### Syntax + +```sql +RANGEEND ( $range ) +``` + +`$range` specifies the range parameter. + +##### Example + +Using the same data as in the [RANGEBEGIN example](#oql-rangebegin-example), this query uses `$range` to retrieve all periods that end before the end value of `$range`: + +```sql +SELECT End, Revenue FROM Sales.Period +WHERE End < RANGEEND($range) +``` + +| End | Revenue | +|---------------------|--------:| +| 2024-06-02 15:12:45 | 10 | + +This query uses `$range_past` to retrieve all periods that ended before the end date of `$range_past`: + +```sql +SELECT End, Revenue FROM Sales.Period +WHERE End < RANGEEND($range_past) +``` + +| End | Revenue | +|---------------------|--------:| +| 2024-06-02 15:12:45 | 10 | + +### REPLACE + +The REPLACE function takes an input string and replaces all occurrences of a specified string within it with another string. The function supports limited and unlimited `STRING` types. Arguments of other types are not supported. + +#### Syntax + +The syntax is as follows: + +```sql +REPLACE ( expression, pattern, replacement ) +``` + +`expression` specifies the string to be searched. + +`pattern` specifies the substring to search for. In the function output, all occurrences of the substring will be replaced with the value of `replacement`. + +`replacement` specifies the string to replace the pattern. + +#### Database-specific limitations + +The behavior of the `REPLACE` function relies on underlying database implementation, which varies by database vendor. For most supported databases, the default behavior of `REPLACE` is case-sensitive. That means that `REPLACE('ABC abc', 'abc', 'xyz')` results in `'ABC xyz'`. In some configurations, the behavior is case-insensitive. For example, for SQL Server, case sensitivity of `REPLACE` depends on which collation is used. + +#### Examples + +The function is useful if you want to format strings in a consistent manner. + +For example, a space delimited list can be converted to one with commas to be used for csv. Assume we have an entity `Sales.Raw` that contains a `STRING` field: + +```sql +SELECT * FROM Sales.Raw +``` + +| ID | Import | +|----|-------------------| +| - | "6 D10 machinery" | +| - | "1 A15 tools" | + +The text can be converted with `REPLACE` as follows: + +```sql +SELECT REPLACE(Import, ' ', ',') FROM Sales.Raw +``` + +| Import | +|-------------------| +| "6,D10,machinery" | +| "1,A15,tools" | + +### ROUND{#round} + +Rounds a numeric `expression` by reducing precision after the decimal point. + +#### Syntax + +The syntax is as follows: + +```sql +ROUND ( expression , length ) +``` + +##### expression + +`expression` is any numeric expression to be rounded. If `expression` is `NULL`, the function will return `NULL`. + +##### length + +`length` specifies the number of decimal places to which the `expression` must be rounded. It must be of a numeric type. If the `length` is `NULL`, the function result will be `NULL`. + +#### Examples + +The function can be used to check the equality of decimal values. In this query a small difference between decimal columns means that no results are returned: + +```sql +SELECT LastName, Number FROM Sales.Order WHERE Price = 1.50000001 +``` + +You can modify it with the use of `ROUND` to only compare to two decimal places: + +```sql +SELECT LastName, Price FROM Sales.Order WHERE ROUND(Price, 2) = ROUND(1.50000001, 2) +``` + +| LastName | Price | +|----------|------:| +| Doe | 1.5 | + +Operations like division with `DECIMAL` data type can produce a large number of digits after the decimal point. `ROUND` can be used to reduce the precision when these are not needed: + +```sql +SELECT ROUND((Price : 7), 2) as RoundedPrice, Price : 7 FROM Sales.Order +``` + +| RoundedPrice | Price | +|-------------:|-----------:| +| 0.21 | 0.21428571 | +| 0.33 | 3.33333333 | +| 1.17 | 1.17142857 | + +### UPPER + +Converts all lowercase characters in a given string to uppercase. Opposite of [LOWER](#lower-function). + +#### Syntax + +The syntax is as follows: + +```sql +UPPER ( expression ) +``` + +`expression` specifies the string to convert. diff --git a/content/en/docs/refguide/modeling/domain-model/oql/oql-expressions.md b/content/en/docs/refguide/modeling/domain-model/oql/oql-expressions.md new file mode 100644 index 00000000000..318dd609421 --- /dev/null +++ b/content/en/docs/refguide/modeling/domain-model/oql/oql-expressions.md @@ -0,0 +1,291 @@ +--- +title: "OQL Expressions" +weight: 20 +url: /refguide/oql-expressions/ +aliases: + - /refguide/oql-aggregation/ + - /refguide/oql-parameters/ + +--- + +## Introduction + +An OQL expression is a query building block that returns a value or a list of values. Expressions can be one of the following: + +* a constant +* a function +* a system variable +* a subquery +* a combination of attribute names, constants, system variables, functions, and subqueries connected by operators + +OQL expressions can be used in `WHERE`, `SELECT`, `GROUP BY`, `UNION`, `HAVING`, and `ON` conditions of `JOIN` clauses. For more information, see [OQL clauses](/refguide/oql-clauses/). + +## Aggregations{#aggregates} + +Aggregations are functions that reduce a list of values from a retrieved column (or columns) into a single value. They can be used in the following ways: + +* as an attribute in a `SELECT` clause +* as a condition in a `HAVING` clause + +When combined with a `GROUP BY` clause, aggregations in `SELECT` clauses can be used with any column normally available, not just those specified in the `GROUP BY` clause. Aggregation is performed over groups separately in this case. + +### Syntax + +```sql + COUNT ( * ) + | { COUNT | AVG | MAX | MIN | SUM } ( [ DISTINCT ] attribute_path ) + | STRING_AGG ( attribute_path ) +``` + +Where `attribute_path` is an attribute reachable from entities defined in the `FROM` and `JOIN` clauses. + +#### COUNT + +Calculates the number of rows in a column. Counting multiple columns using `COUNT(*)` returns a count of all rows. + +When counting a single column, rows with a `NULL` value are not counted. + +#### AVG + +Calculates the mean of numerical (`INTEGER`, `DECIMAL`, and `LONG`) values. `NULL` values are ignored. + +#### MAX and MIN + +Returns the maximum or minimum value from a column, with all data types being supported. + +* Boolean values are treated as `0` and `1` +* Strings are compared alphabetically + +`NULL` values are ignored. + +#### SUM + +Calculates the sum of numerical values. + +`NULL` values are ignored. + +#### STRING_AGG + +Combines multiple string values from a specified column into a single string. Each value is separated from the next by a specified separator string. + +`NULL` is ignored, empty strings are included. + +The syntax of STRING_AGG differs from other aggregations and is as follows: + +```sql +STRING_AGG ( attribute_path, separator ) +``` + +`separator` is any expression of type `STRING`. + +{{% alert color="info" %}} +This aggregate function is only supported in Java actions. +{{% /alert %}} + +### Examples + +In the following examples, the `Sales.Product` entity has five objects with `Name` and `Stock` attributes: + +```sql +SELECT Name, Stock FROM Sales.Product +``` + +| Name | Stock | +|----------|-------| +| Cheese | 5 | +| Milk | 54 | +| Tomatoes | 44 | +| Tomatoes | 44 | +| Tomatoes | NULL | + +#### COUNT + +The number of rows can be calculated with `COUNT`: + +```sql +SELECT COUNT(*) AS ProductCount FROM Sales.Product +``` + +| ProductCount | +|:------------:| +| 5 | + +The same result can be retrieved by using `COUNT` on a single attribute: + +```sql +SELECT COUNT(Name) AS NameEntryCount FROM Sales.Product +``` + +As there is a `NULL` value in the `Stock` column, when using `COUNT` it will be ignored: + +```sql +SELECT COUNT(Stock) AS StockEntryCount FROM Sales.Product +``` + +| StockEntryCount | +|:---------------:| +| 4 | + +You can count just unique names with [`DISTINCT`](/refguide/oql-clauses/#Distinct): + +```sql +SELECT COUNT(DISTINCT Name) AS DistinctNameEntryCount FROM Sales.Product +``` + +| DistinctNameEntryCount | +|:----------------------:| +| 3 | + +#### AVG + +The average stock per product entry: + +```sql +SELECT AVG(Stock) AS StockAverage FROM Sales.Product +``` + +| StockAverage | +|:-------------:| +| 36.75 | + +There are duplicate values in the `Stock` column, which can be ignored by using `DISTINCT`. The query below returns the average unique stock: + +```sql +SELECT AVG(Distinct Stock) AS DistinctStockAverage FROM Sales.Product +``` + +| DistinctStockAverage | +|:--------------------:| +| 33.333 | + +#### MAX + +The most stock of any one product: + +```sql +SELECT MAX(Stock) as StockMax FROM Sales.Product +``` + +| StockMax | +|:--------:| +| 54 | + +To return the name(s) of the product(s) with the highest stock level you have to use a subquery. The subquery returns the maximum stock number, which is then compared to each product's stock in the `WHERE` clause: + +```sql +SELECT HighestStockProductName FROM Sales.Product +WHERE Stock = (SELECT MAX(P.Stock) FROM Sales.Product P) +``` + +| HighestStockProductName | +|:-----------------------:| +| Milk | + +#### SUM + +The sum of all products in stock: + +```sql +SELECT Sum(Stock) AS StockSum FROM Sales.Product +``` + +| StockSum | +|:--------:| +| 147 | + +The sum of unique product entries: + +```sql +SELECT Sum(DISTINCT Stock) AS DistinctStockSum FROM Sales.Product +``` + +| DistinctStockSum | +|:----------------:| +| 103 | + +#### STRING_AGG + +You can aggregate product names into a single list: + +```sql +SELECT STRING_AGG(Name, ',') as ProductNames FROM Sales.Product +``` + +| ProductNames | +|:-----------------------------:| +| Cheese,Milk,Tomatoes,Tomatoes | + +## Parameters + +Parameters are external variables that are referenced to by name in an OQL query. To use a defined parameter in a query, prepend the `$` sign to the name of the parameter. + +If you use undefined in `IN` and `LIKE` comparison expressions, the condition always returns `true`. In other cases, undefined parameters cause an exception. + +{{% alert color="warning" %}} Parameters are only supported within OQL queries defined in [data sets](/refguide/data-sets/) or inside Java actions using the [Mendix Runtime API](/apidocs-mxsdk/apidocs/runtime-api/). They can not be used in View entities {{% /alert %}} + +### Examples + +#### Defined Parameters + +In these examples the valid parameters `$age` and `$limit`, both of type `INTEGER`, have been defined. + +The following query uses the parameter in the `WHERE` clause, returning a result set with person names whose age is higher than the value specified in the `$age` parameter. + +```sql +SELECT Name +FROM Sales.Person +WHERE + Age > $age +``` + +The following query will return a limited number of rows, limited by the value of the `$limit` parameter in the `LIMIT` clause. + +```sql +SELECT Name +FROM Sales.Person +LIMIT $limit +``` + +#### Undefined Parameters + +As an example, take a query with two comparisons using the parameter `$param`: + +```sql +SELECT Name +FROM Sales.Person +WHERE + Age > $param + OR + Job = 'Sales' +``` + +If the value of `$param` is not provided as a parameter to the query, the query will be equivalent to: + +```sql +SELECT Name +FROM Sales.Person +WHERE + TRUE + OR + Job = 'Sales' +``` + +The example above is different from the case where the value of `$param` is provided, but is `NULL`. In that case, the query will be equivalent to: + +```sql +SELECT Name +FROM Sales.Person +WHERE + Age > NULL + OR + Job = 'Sales' +``` + +If you use an undefined parameter used in a `LIMIT`, will throw an exception. + +```sql +SELECT Name +FROM Sales.Person +ORDER BY LastName +LIMIT $param +``` diff --git a/content/en/docs/refguide/modeling/domain-model/oql/oql-v2.md b/content/en/docs/refguide/modeling/domain-model/oql/oql-v2.md new file mode 100644 index 00000000000..db34e422627 --- /dev/null +++ b/content/en/docs/refguide/modeling/domain-model/oql/oql-v2.md @@ -0,0 +1,278 @@ +--- +title: "OQL Version 2 Features" +linktitle: "Switching to OQL Version 2" +url: /refguide/oql-v2/ +weight: 100 +--- + +## Introduction + +In Studio Pro version 10.19, we introduced OQL v2. It can be enabled by setting the [OQL version 2](/refguide/app-settings/#oql-version-2) app setting to *Yes*. You will have to switch to OQL v2 in order to enable [View entities](/refguide/view-entities/). + +OQL v2 syntax is the same as OQL. However, there are a few differences in the handling of specific, mostly not very common, cases. + +When switching to OQL v2, you should check if existing OQL queries in your model have any of these features. + +## OQL V2 Features + +The following changes are included in OQL v2: + +### `GROUP BY` Association Is No Longer Allowed + +In OQL v2, you can no longer use a path over association in a `GROUP BY` query because the outcome of such query was unpredictable in the case of one-to-many and many-to-many associations. + +This query is not allowed anymore: + +```sql +SELECT COUNT(*) AS count +FROM Module.Person +GROUP BY Module.Person/Module.Person_City/Module.City/Name +``` + +Instead, you can use the long path in the `JOIN` pattern as follows: + +```sql +SELECT COUNT(*) AS count +FROM Module.Person AS P +JOIN Module.Person/Module.Person_City/Module.City AS C +GROUP BY C/Name +``` + +### More Strict Data Type Validation in OQL Functions + +OQL v2 has more strict data type validation of some functions and other expressions. + +#### `CASE` {#case-validations} + +In OQL v2, all result expressions of a [CASE](/refguide/oql-expression-syntax/#case-expression) expression should have matching types or be Null. In OQL v1, there was no such validation, and handling of different types was delegated to the database, which made the behavior database-specific and unreliable. + +Also, in OQL v2 it is no longer possible to have Null as the result of all result expressions of CASE. In OQL v1, that was allowed, but would lead to database-level exceptions for some database vendors. + +Numeric types Integer, Long and Decimal are considered matching. See [examples](/refguide/oql-expression-syntax/#case-expression-examples) of `CASE` expressions for details on how different numeric types are combined. + +#### `COALESCE` {#coalesce-validations} + +Data type validation of [COALESCE](/refguide/oql-expression-syntax/#coalesce-expression) follows the same logic as CASE. + +In OQL v2, all arguments of a [COALESCE](/refguide/oql-expression-syntax/#coalesce-expression) expression should have matching types or be Null. In OQL v1, there was no such validation, and handling of different types was delegated to the database, which made the behavior database-specific and unreliable. + +Also, in OQL v2 it is no longer possible to have a COALESCE expression where all arguments are Null literals. In OQL v1, that was allowed, but would lead to database-level exceptions for some database vendors. + +Numeric types Integer, Long and Decimal are considered matching. See [examples](/refguide/oql-expression-syntax/#coalesce-expression-examples) of `COALESCE` for details on how different numeric types are combined. + +#### `LENGTH` {#length-validations} + +In OQL v2, the argument of [LENGTH](/refguide/oql-expression-syntax/#length-function) function can only be of type String. Note that values of Enumerations are also treated as Strings. In OQL v1, there was no such validation, and handling of other types was delegated to the database. + +#### `DATEPART` and `DATEDIFF` {#date-validations} + +In OQL v2, the date arguments of functions [DATEPART](/refguide/oql-expression-syntax/#datepart-function) and [DATEDIFF](/refguide/oql-expression-syntax/#datediff-function) can be only of one of the following types: + +* Date and time +* String. In this case, Runtime will implicitly attempt conversion of the string to a Date and time. +* One of Numeric types Integer, Long and Decimal. In this case, the value is treated as a Java timestamp. + +### Subquery Columns Should Have a Name or an Alias + +In OQL v1, when a column in the `SELECT` clause is a subquery, it was possible for it not to have a name. For example: + +```sql +SELECT + Name, + ( + SELECT COUNT(*) + FROM Module.Person + WHERE Module.Person/City = Module.City/Name + ) +FROM Module.City +``` + +The column names in this case would be `Name` and a `null` name which would not be possible to refer to. + +In OQL v2, such queries are no longer allowed. You must always provide an alias for subqueries that do not result in a named column. The query above can be rewritten as follows: + +```sql +SELECT + Name, + ( + SELECT COUNT(*) + FROM Module.Person + WHERE Module.Person/City = Module.City/Name + ) AS PersonCount +FROM Module.City +``` + +This also applies to constants as attributes, including `NULL`. The query below is no longer allowed: + +```sql +SELECT + Name, + NULL +FROM Module.City +``` + +It can be rewritten to use aliases for constants: + +```sql +SELECT + Name, + NULL as NullColumn +FROM Module.City +``` + +{{% alert color="info" %}} +This requirement only applies to attributes in the main `SELECT` query. `SELECT` clauses inside subqueries are not affected, as are columns that are used for comparisons like in a `WHERE` clause. +{{% /alert %}} + +### Duplicate Columns in `SELECT` + +#### Specify Entity Name + + You can no longer use duplicate column names without specifying an entity name when it is not clear which entity the attribute belongs to. You must explicitly specify the entity. + +For example, if both entities `Module.Person` and `Module.City` have an attribute `Name`, you cannot use the following query in OQL v2: + +```sql +SELECT Name +FROM Module.Person +JOIN Module.Person/Module.Person_City/Module.City +``` + +In OQL v1, the query would assume that `Name` belongs to `Module.Person`, which could become a source of errors. + +In OQL v2, you can write the same query specifying the entity, as follows: + +```sql +SELECT Module.Person/Name +FROM Module.Person +JOIN Module.Person/Module.Person_City/Module.City +``` + +#### Allow Joining of Subqueries with Duplicate Columns + +In OQL v1, if both entities `Module.Person` and `Module.City` have a duplicate attribute `Name`, the following query would fail: + +```sql +SELECT * +FROM (SELECT * FROM Module.Person) P +JOIN (SELECT * FROM Module.City) C +ON P/Residence = C/Name +``` + +In OQL v2, the query above no longer fails, which makes the behavior consistent with the case of `SELECT *` combined with `JOIN` without subqueries. + +Having concrete duplicate attribute names is also now allowed: + +```sql +SELECT * +FROM (SELECT Name FROM Module.Person) P +JOIN (SELECT Name FROM Module.City) C +ON P/Residence = C/Name +``` + +You still cannot use duplicate aliases in different joined subqueries. This leads to an error the same way as in OQL v1. + +For example, you cannot use the following query: + +```sql +SELECT * +FROM (SELECT LastName AS N FROM Module.Person) P +JOIN (SELECT Name AS N FROM Module.City) C +ON P/Residence = C/Name +``` + +### `ORDER BY` in Subquery + +You must now have a `LIMIT` and/or `OFFSET` in subquery containing `ORDER BY`. Using `ORDER BY` in subquery makes sense only when it is combined with `LIMIT` and/or `OFFSET`. Without the limitations, database engines do not guarantee that the row order in the subquery will be preserved in the outer query. + +Consequently, you cannot use the following query in OQL v2. + +```sql +SELECT * +FROM ( + SELECT Name + FROM Module.Person + ORDER BY Name +) +``` + +In OQL v1, Runtime would have passed such query to the database and. although it would have been handled by most database engines, it could not be handled by SQL Server. + +You can still use a subquery with `LIMIT` and/or `OFFSET`. For instance, in the following query, the subquery returns the first 20 objects of entity `Module.Person` ordered by `Name`: + +```sql +SELECT * +FROM ( + SELECT Name + FROM Module.Person + ORDER BY Name + LIMIT 20 +) +``` + +### `ORDER BY` in View Entities + +For view entities, you must now have a `LIMIT` and `OFFSET` in all `ORDER BY` clauses, even for the top level query. + +This is because view entity results are not accessed directly. In the Runtime, a View entity is wrapped inside another `SELECT` query as a subquery, which allows further filtering and ordering. This makes it similar to the Subquery case, above. + +### Result Types from Arithmetic Functions + +OQL v2 handles the situations where different sides of an arithmetic operation have different data types differently. + +Take the following example: + +```sql +SELECT Attribute1 + Attribute2 AS SumAttr +FROM Module.Entity +``` + +In OQL v1, the result of the arithmetic operation will always be of type pf the first attribute in the expression because it is handled by the database. Therefore, the result would depend on the underlying database engine. + +When handling numeric types in OQL v2 (Integer, Long, and Decimal), the result of the operation is always the most precise attribute type, using the following precedence: + +* Decimal (highest) +* Long +* Integer + +If any side of the operation is of a non-numeric type, no casting is performed, and the result is handled by the , as in OQL v1. See [Expression syntax](/refguide/oql-expression-syntax/#type-coercion) for more information. + +### The Result Type of `ROUND` Is Now `Decimal` + +In OQL v1, `ROUND` would return a `Float` result. 'Float' is no-longer supported by Studio Pro. In OQL v2, it always returns `Decimal`. + +### Attribute Alias in `WHERE` + +In OQL v1, referring to an attribute by alias was allowed, but it would throw an exception in the database. In OQL v2, that is no longer allowed. + +### `JOIN` Without `ON` + +In OQL v1, it was possible to write a query such as the following.: + +```sql +SELECT * +FROM Module.Person +JOIN Module.City +``` + +This is no longer allowed in OQL v2 as a query like this would fail in every supported database engine except MySQL. + +In OQL v2, you must either specify `ON` or use join over association. So you could rewrite the query above as follows: + +```sql +SELECT * +FROM Module.Person, Module.City +``` + +### `JOIN` an Entity to Its Own Generalization + +In OQL v1 there was a bug which meant that when you `JOIN`ed an entity to its own generalization it would generate unexpected specialization columns. This has been fixed. + +For example, say entity `Module.Vehicle` has two specializations: `Module.Car` and `Module.Bike`. The query below would previously generate unexpected duplicate columns for entity `Vehicle`. This no longer happens. + +```sql +SELECT * +FROM Module.Car +JOIN Module.Vehicle +ON True +``` diff --git a/content/en/docs/refguide/modeling/mendix-ai-assistance/domain-model-generator.md b/content/en/docs/refguide/modeling/mendix-ai-assistance/domain-model-generator.md index ae3b8120577..cad828b1d88 100644 --- a/content/en/docs/refguide/modeling/mendix-ai-assistance/domain-model-generator.md +++ b/content/en/docs/refguide/modeling/mendix-ai-assistance/domain-model-generator.md @@ -78,6 +78,6 @@ Currently, it cannot set [generalization](/refguide/generalization-and-associati ## Read More -* [Domain Model](/refguide/domain-model/) +* [Data in the Domain Model](/refguide/domain-model/) * [Mendix AI Assistance (Maia)](/refguide/mendix-ai-assistance/) * [Maia Chat](/refguide/maia-chat/) diff --git a/content/en/docs/refguide/modeling/menus/edit-menu/go-to-option.md b/content/en/docs/refguide/modeling/menus/edit-menu/go-to-option.md index f24b2bb44fe..05a6b579813 100644 --- a/content/en/docs/refguide/modeling/menus/edit-menu/go-to-option.md +++ b/content/en/docs/refguide/modeling/menus/edit-menu/go-to-option.md @@ -31,4 +31,4 @@ The examples of using the **Go to** option are described below: * [Navigation](/refguide/navigation/) * [Pages](/refguide/pages/) * [Microflows](/refguide/microflows/) -* [Domain Model](/refguide/domain-model/) +* [Data in the Domain Model](/refguide/domain-model/) diff --git a/content/en/docs/refguide/modeling/pages/data-widgets/configure-form.md b/content/en/docs/refguide/modeling/pages/data-widgets/configure-form.md index 7d64cbcc6e1..d9eb1b1436b 100644 --- a/content/en/docs/refguide/modeling/pages/data-widgets/configure-form.md +++ b/content/en/docs/refguide/modeling/pages/data-widgets/configure-form.md @@ -43,7 +43,7 @@ You would like the **Details** button in this list to open a page showing the de Before starting this how-to, make sure you have completed the following prerequisites: * Familiarize yourself with page terms and how to perform basic functions on pages. For more information, see [Page](/refguide/page/). -* Familiarize yourself with the domain model terms and learn how to perform basic functions. For more information, see [Domain Model](/refguide/domain-model/). +* Familiarize yourself with the domain model terms and learn how to perform basic functions. For more information, see [Data in the Domain Model](/refguide/domain-model/). * Make sure your domain model is configured the following way: {{< figure src="/attachments/refguide/modeling/pages/data-widgets/configure-form/domain-model.jpg" alt="Domain Model" class="no-border" >}} diff --git a/content/en/docs/refguide/modeling/pages/data-widgets/configure-list-and-details-on-one-page.md b/content/en/docs/refguide/modeling/pages/data-widgets/configure-list-and-details-on-one-page.md index 90a67f167ee..34cff3ab958 100644 --- a/content/en/docs/refguide/modeling/pages/data-widgets/configure-list-and-details-on-one-page.md +++ b/content/en/docs/refguide/modeling/pages/data-widgets/configure-list-and-details-on-one-page.md @@ -26,7 +26,7 @@ Sales Representatives in your company want to view a list of opportunity contact Before starting this how-to, make sure you have completed the following prerequisites: -* Familiarize yourself with the domain model terms and learn how to perform basic functions. For more information, see [Domain Model](/refguide/domain-model/). +* Familiarize yourself with the domain model terms and learn how to perform basic functions. For more information, see [Data in the Domain Model](/refguide/domain-model/). * Make sure your domain model is configured in the following way: {{< figure src="/attachments/refguide/modeling/pages/data-widgets/configure-list-and-details-on-one-page/domain-model.png" width="200px" class="no-border" >}} diff --git a/content/en/docs/refguide/modeling/pages/image-and-file-widgets/attach-images.md b/content/en/docs/refguide/modeling/pages/image-and-file-widgets/attach-images.md index f82d7d11d14..a367ea2a46c 100644 --- a/content/en/docs/refguide/modeling/pages/image-and-file-widgets/attach-images.md +++ b/content/en/docs/refguide/modeling/pages/image-and-file-widgets/attach-images.md @@ -30,7 +30,7 @@ Before starting this how-to, make sure you have completed the following prerequi * Familiarize yourself with page terms and how to perform basic functions on pages. For more information, see [Page](/refguide/page/). -* Familiarize yourself with the domain model terms and learn how to perform basic functions. For more information, see [Domain Model](/refguide/domain-model/). +* Familiarize yourself with the domain model terms and learn how to perform basic functions. For more information, see [Domain Model]. * Make sure your domain model is configured the following way: diff --git a/content/en/docs/refguide/modeling/pages/image-and-file-widgets/configure-files.md b/content/en/docs/refguide/modeling/pages/image-and-file-widgets/configure-files.md index 5aec7bd4c33..bb6f8ab18b1 100644 --- a/content/en/docs/refguide/modeling/pages/image-and-file-widgets/configure-files.md +++ b/content/en/docs/refguide/modeling/pages/image-and-file-widgets/configure-files.md @@ -31,7 +31,7 @@ Before starting this how-to, make sure you have completed the following prerequi * Familiarize yourself with page terms and how to perform basic functions on pages. For more information, see [Page](/refguide/page/). -* Familiarize yourself with the domain model terms and learn how to perform basic functions. For more information, see [Domain Model](/refguide/domain-model/). +* Familiarize yourself with the domain model terms and learn how to perform basic functions. For more information, see [Data in the Domain Model](/refguide/domain-model/). * Make sure your domain model is configured the following way: diff --git a/content/en/docs/refguide/modeling/resources/_index.md b/content/en/docs/refguide/modeling/resources/_index.md index efe0f7ab964..74f1b1f94c8 100644 --- a/content/en/docs/refguide/modeling/resources/_index.md +++ b/content/en/docs/refguide/modeling/resources/_index.md @@ -41,5 +41,5 @@ The **Resources** category contain various document types that can be used in di ## Read More * [Microflows](/refguide/microflows/) -* [Domain Model](/refguide/domain-model/) +* [Domain Model] * [Pages](/refguide/pages/) diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/_index.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/_index.md deleted file mode 100644 index 7e02144ed03..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/_index.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: "OQL" -url: /refguide/oql/ ---- - -## Introduction - -The Mendix Object Query Language (OQL) is a relational query language just like [SQL](https://en.wikipedia.org/wiki/Sql). The major advantage of OQL is that it uses entity and association names instead of actual database table names. - -In addition, OQL can use predefined relations (associations) to easily join objects without having to calculate which columns should be coupled. Despite these differences, many SQL keywords also work in OQL. - -These are some examples of OQL queries: - -* `SELECT Name FROM Sales.Customer` – retrieves the names of all customers -* `SELECT FirstName FROM Sales.Customer WHERE Name = 'Jansen'` – retrieves the first name of all customers with name "Jansen" -* `SELECT AVG(TotalPrice) FROM Sales."Order" WHERE IsPaid = 1` – retrieves the average of the total prices of all paid orders (`Order` needs to be wrapped in quotes, see the [Reserved Words](#reserved-oql-words) section below) - -{{% alert color="info" %}} -OQL queries do not take security into account out-of-the-box. This means that you can use OQL to manually define custom security expressions. In some cases, handling security yourself using OQL—instead of using the out-of-the-box security of XPath—may result in faster queries. -{{% /alert %}} - -Try your OQL example online with the [OQL Playground](https://service.mendixcloud.com/p/OQL) demo app. - -## Query Components - -An OQL query can use these components: - -| Query Part | OQL | Purpose | -| --- | --- | --- | -| [Select clause](/refguide/oql-select-clause/) (required) | `SELECT AVG(TotalPrice)` | Determines which attributes of the object being queried are retrieved. Any functions that need to be performed on the retrieved data should also be defined here. | -| [From clause](/refguide/oql-from-clause/) (required) | `FROM Sales."Order"` | Designates the source entity from which the data will be retrieved. | -| [Where clause](/refguide/oql-where-clause/) (optional) | `WHERE IsPaid = 1` | Constrains the data being retrieved. | -| [Group by clause](/refguide/oql-group-by-clause/) (optional) | `GROUP BY Department` | Groups rows on the values of the specified attributes. | -| [Order by clause](/refguide/oql-order-by-clause/) (optional) | `ORDER BY Date` | Sorts rows on the specified attributes. | -| [Limit clause](/refguide/oql-limit-clause/) (optional) | `LIMIT 50 OFFSET 30` | Limits rows to a subset of the total amount. | - -## Reserved Words {#reserved-oql-words} - -Words with a specific purpose in OQL are reserved. If you use reserved words for entity, variable or attribute names in an OQL query, they must be wrapped in double quotes `" "`. For example, in the OQL query `SELECT AVG(TotalPrice) FROM Sales."Order" WHERE IsPaid = 1`, `Order` needs to be wrapped in quotes because it is a reserved word, as it can be used for `ORDER BY`. - -Here is a list of all OQL reserved words: - -* `ALL` -* `AND` -* `AS` -* `ASC` -* `AVG` -* `BOOLEAN` -* `BY` -* `CASE` -* `CAST` -* `COALESCE` -* `COUNT` -* `DATEDIFF` -* `DATEPART` -* `DATETIME` -* `DAY` -* `DAYOFYEAR` -* `DECIMAL` -* `DESC` -* `DISTINCT` -* `ELSE` -* `END` -* `EXISTS` -* `FALSE` -* `FLOAT` -* `FROM` -* `FULL` -* `GROUP` -* `HAVING` -* `HOUR` -* `IN` -* `INNER` -* `INTEGER` -* `IS` -* `JOIN` -* `LEFT` -* `LENGTH` -* `LIKE` -* `LIMIT` -* `LONG` -* `MAX` -* `MILLISECOND` -* `MIN` -* `MINUTE` -* `MONTH` -* `NEXTVALUE` -* `NOT` -* `NULL` -* `OFFSET` -* `ON` -* `OR` -* `ORDER` -* `OUTER` -* `QUARTER` -* `RANGEBEGIN` -* `RANGEEND` -* `RIGHT` -* `ROUND` -* `SECOND` -* `SELECT` -* `STRING` -* `SUM` -* `THEN` -* `TRUE` -* `UNION` -* `WEEK` -* `WEEKDAY` -* `WHEN` -* `WHERE` -* `YEAR` diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/_index.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/_index.md deleted file mode 100644 index afbf1e6f4bc..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/_index.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "OQL Expressions" -url: /refguide/oql-expressions/ ---- - -An expression is either a constant, a function, or any combination of attribute names, constants, and functions connected by operator (or operators), or a subquery. For more information, see the pages below. - -* [OQL Aggregation](/refguide/oql-aggregation/) -* [OQL Functions](/refguide/oql-functions/) -* [OQL Operators](/refguide/oql-operators/) -* [OQL Parameters](/refguide/oql-parameters/) -* [OQL Subqueries](/refguide/oql-subqueries/) diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-aggregation.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-aggregation.md deleted file mode 100644 index 196bc991a63..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-aggregation.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: "OQL Aggregation" -url: /refguide/oql-aggregation/ ---- - - -Aggregations perform specific calculations on the values of the retrieved column (or columns). The following aggregate functions are possible: - -| Expression | Description | -| --- | --- | -| AVG | Average | -| COUNT | Count | -| MAX | Maximum | -| MIN | Minimum | -| SUM | Sum | - -When you are using an aggregate expression in the `SELECT` clause, all expressions in the `SELECT` clause have to be either an aggregation *or* part of the `GROUP BY` clause of the query. diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/_index.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/_index.md deleted file mode 100644 index de9c0d27f61..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/_index.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: "OQL Functions" -url: /refguide/oql-functions/ ---- - -For more information on OQL functions, see the pages below. - -* [OQL CAST](/refguide/oql-cast/) -* [OQL COALESCE](/refguide/oql-coalesce/) -* [OQL DATEDIFF](/refguide/oql-datediff/) -* [OQL DATEPART](/refguide/oql-datepart/) -* [OQL LENGTH](/refguide/oql-length/) -* [OQL LOWER](/refguide/oql-lower/) -* [OQL RANGEBEGIN](/refguide/oql-rangebegin/) -* [OQL RANGEEND](/refguide/oql-rangeend/) -* [OQL REPLACE](/refguide/oql-replace/) -* [OQL ROUND](/refguide/oql-round/) -* [OQL UPPER](/refguide/oql-upper/) diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-cast.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-cast.md deleted file mode 100644 index f8f98e4a890..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-cast.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: "OQL CAST" -url: /refguide/oql-cast/ ---- - -## Description - -The `CAST` function converts an expression to a specific data type. - -## Syntax - -The syntax is as follows: - -```sql -CAST ( expression AS data_type ) -``` - -### expression - -`expression` specifies the expression to convert. - -### data_type - -`data_type` specifies the data type to convert the expression to. The data type can be one of the following: - -* `BOOLEAN` -* `DATETIME` -* `DECIMAL` -* `INTEGER` -* `LONG` -* `STRING` - -## Supported Conversions - -The table below describes which `CAST` conversions are supported: - -* ✔ – the conversion is supported -* ✔* – the conversion is supported, but the behavior differs per database -* ✘ – the conversion is not supported - -| From \ To | BOOLEAN | DATETIME | DECIMAL | INTEGER | LONG | STRING (unlimited) | STRING (limited) | -|------| :------: | :------: | :------: | :------: | :------: | :------: | :------: | -| BOOLEAN | ✔ | ✘ | ✘ | ✘ | ✘ | ✔* | ✔*1 | -| DATETIME | ✘ | ✔ | ✘ | ✘ | ✘ | ✔* | ✔*2 | -| DECIMAL | ✘ | ✘ | ✔* | ✔* | ✔* | ✔* | ✔*2 | -| INTEGER | ✘ | ✘ | ✔ | ✔ | ✔ | ✔ | ✔ | -| LONG | ✘ | ✘ | ✔ | ✔ | ✔ | ✔ | ✔ | -| STRING | ✘ | ✘ | ✔ | ✔ | ✔ | ✔ | ✔ | - -[1] BOOLEAN to STRING (limited) is supported only if the resulting string length is ≥ 5.
[2] The conversion of DATETIME and DECIMAL to STRING (limited) is supported only if the value fully fits into the string length. The conversion can fail if the resulting string length is < 20.
- -## Examples - -A frequent use case for `CAST` is to convert your date from the `DATETIME` data type to a more readable `STRING` type: - -```sql -CAST ( your_datetime_variable AS string ) -``` diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-coalesce.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-coalesce.md deleted file mode 100644 index 0f76a91f2a4..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-coalesce.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: "OQL COALESCE" -url: /refguide/oql-coalesce/ ---- - -## Description - -The `COALESCE` function returns the first of its arguments that is not NULL. - -## Syntax - -The syntax is as follows: - -```sql -COALESCE ( expression [ ,...n ] ) -``` - -`expression` specifies the expression to check, if the result is NULL. diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-datediff.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-datediff.md deleted file mode 100644 index a87fe9eddab..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-datediff.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: "OQL DATEDIFF" -url: /refguide/oql-datediff/ ---- - -## Description - -The `DATEDIFF` function returns the difference between two given date/time values. The difference is given in the specified unit. - -## Syntax - -The syntax is as follows: - -```sql -DATEDIFF ( unit , startdate_expression, enddate_expression [, timezone ] ) -``` - -### unit - -`unit` specifies the unit of the date/time value to retrieve. This can be one of the following: -`YEAR`, `QUARTER`, `MONTH`, `DAY`, `WEEK`, `HOUR`, `MINUTE` or `SECOND`. For more information on date/time values, see the [Example](/refguide/oql-datepart/#oql-datepart-example) section in *OQL DATEPART*. - -### startdate_expression - -`startdate_expression` specifies the start date of the period being calculated. This should be formatted in an expression which resolves to a date/time value. - -### enddate_expression - -`enddate_expression` specifies the end date of the period being calculated. This should be formatted in an expression which resolves to a date/time value. - -### timezone - -`timezone` specifies the time zone to use for the retrieval. This parameter is optional and defaults to the local time zone. It should be a string literal containing an IANA time zone. GMT offset time zones are not supported. diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-datepart.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-datepart.md deleted file mode 100644 index 5d7cad4e7fb..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-datepart.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: "OQL DATEPART" -url: /refguide/oql-datepart/ ---- - -## Description - -The `DATEPART` function retrieves a specified element from a date/time values. This element is of type integer. - -## Syntax - -The syntax is as follows: - -```sql -DATEPART ( datepart , date_expression [, timezone ] ) -``` - -### datepart - -`datepart` specifies the part of the date/time value to retrieve. For possible values, see the [Example](#oql-datepart-example) below. - -### date_expression - -`date_expression` specifies the date to retrieve an element from. This should be formatted in an expression which resolves to a date/time value. - -### timezone - -`timezone` specifies the time zone to use for the retrieval. This parameter is optional and defaults to the local time zone. It should be a string literal containing an IANA time zone. GMT offset time zones are not supported. - -## Example{#oql-datepart-example} - -| datepart | Definition | Example (Friday July 1, 2005, 16:34:20) | -| --- | --- | --- | -| `YEAR` | | 2005 | -| `QUARTER` | 1, 2, 3 or 4 | 3 | -| `MONTH` | 1 to 12 | 7 | -| `DAYOFYEAR` | 1 to 366 | | -| `DAY` | 1 to 31 | 5 | -| `WEEK` | 1 to 53 (depends on the database implementation) | | -| `WEEKDAY` | 1 to 7 (1 = Sunday, 7 = Saturday) | 6 | -| `HOUR` | 0 to 23 | 16 | -| `MINUTE` | 0 to 59 | 34 | -| `SECOND` | 0 to 59 | 20 | diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-length.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-length.md deleted file mode 100644 index dc9cfe81c81..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-length.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: "OQL LENGTH" -url: /refguide/oql-length/ ---- - -## Description - -The `LENGTH` function returns the length of a string value. - -## Syntax - -The syntax is as follows: - -```sql -LENGTH ( expression ) -``` - -`expression` specifies an expression of type string. diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-lower.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-lower.md deleted file mode 100644 index 33850b8ad5d..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-lower.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: "OQL LOWER" -url: /refguide/oql-lower/ ---- - -## Description - -The `LOWER` function converts all uppercase characters in a given string to lowercase and returns the result. - -## Syntax - -The syntax is as follows: - -```sql -LOWER ( expression ) -``` - -`expression` specifies the string to convert. diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-rangebegin.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-rangebegin.md deleted file mode 100644 index 3a02245cf12..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-rangebegin.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: "OQL RANGEBEGIN" -url: /refguide/oql-rangebegin/ ---- - -## Description - -The `RANGEBEGIN` function extracts the initial value of a range parameter. - -`RANGEBEGIN` and [RANGEEND](/refguide/oql-rangeend/) are OQL functions that use a parameter, and OQL parameters are only available in [datasets](/refguide/data-sets/) (which are used for generating a report). When you create a page and add a report that has a dataset, you can use `RANGEBEGIN` and `RANGEEND` in that dataset. - -## Syntax - -The syntax is as follows: - -```sql -RANGEBEGIN ( $range ) -``` - -`$range` specifies the range parameter. - -## Example - -This is an example of using a range in OQL, where `$range` is set to last week, which will give you all the customers born in the last week: - -```sql -SELECT FirstName AS First, LastName AS Last, Name AS Name, Birthday AS BDay, CustomerType AS Type FROM Sales.Customer -WHERE Birthday IN ($rangeLastWeek) -``` - -This example uses the `RANGEBEGIN` function in the `WHERE` clause, which will give you all the customers born since the beginning of last week: - -```sql -SELECT FirstName AS First, LastName AS Last, Name AS Name, Birthday AS BDay, CustomerType AS Type FROM Sales.Customer -WHERE Birthday > RANGEBEGIN($rangeLastWeek) -``` diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-rangeend.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-rangeend.md deleted file mode 100644 index 5e81bc44300..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-rangeend.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: "OQL RANGEEND" -url: /refguide/oql-rangeend/ ---- - -## Description - -The `RANGEEND` function extracts the end value of a range parameter. - -[RANGEBEGIN](/refguide/oql-rangebegin/) and `RANGEEND` are OQL functions that use a parameter, and OQL parameters are only available in [datasets](/refguide/data-sets/) (which are used for generating a report). When you create a page and add a report that has a dataset, you can use `RANGEBEGIN` and `RANGEEND` in that dataset. - -## Syntax - -The syntax is as follows: - -```sql -RANGEEND ( $range ) -``` - -`$range` specifies the range parameter. - -## Example - -This is an example of using a range in OQL, where `$range` is set to last week, which will give you all the customers born in the last week: - -```sql -SELECT FirstName AS First, LastName AS Last, Name AS Name, Birthday AS BDay, CustomerType AS Type FROM Sales.Customer -WHERE Birthday IN ($rangeLastWeek) -``` - -This example uses the `RANGEEND` function in the `WHERE` clause, which will give you all the customers born since the end of last week: - -```sql -SELECT FirstName AS First, LastName AS Last, Name AS Name, Birthday AS BDay, CustomerType AS Type FROM Sales.Customer -WHERE Birthday > RANGEEND($rangeLastWeek) -``` diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-replace.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-replace.md deleted file mode 100644 index 8900f9eb999..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-replace.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: "OQL REPLACE" -url: /refguide/oql-replace/ ---- - -## Description - -The `REPLACE` function replaces all occurrences of a specified string value with another string value. The function supports limited and unlimited strings. Arguments of other types are not supported. - -## Syntax - -The syntax is as follows: - -```sql -REPLACE ( expression, pattern, replacement ) -``` - -`expression` specifies the string to be searched. - -`pattern` specifies the pattern to search for. In the function output, all occurrences of the pattern will be replaced with the value of `replacement`. - -`replacement` specifies the string to replace the pattern. - -## Database-specific limitations - -The behavior of the `REPLACE` function relies on underlying database implementation, which may vary by database vendor. For most supported databases, default behavior of `REPLACE` is case-sensitive. That means that `REPLACE('ABC abc', 'abc', 'xyz')` results in `'ABC xyz'`. In some configurations, the behavior is case-insensitive. For example, for SQL Server, case sensitivity of `REPLACE` depends on used collation. diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-round.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-round.md deleted file mode 100644 index 21c4385575f..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-round.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "OQL ROUND" -url: /refguide/oql-round/ ---- - -## Description - -The `ROUND` function rounds a given numeric expression. - -## Syntax - -The syntax is as follows: - -```sql -ROUND ( numeric_expression , length ) -``` - -### numeric_expression - -`numeric_expression` specifies the expression which must be rounded. This expression must be a numeric expression. - -{{% alert color="info" %}} - -If `numeric_expression` is `NULL` (empty), the function will return `NULL`. - -{{% /alert %}} - -### length - -`length` specifies the amount of decimals to which the expression must be rounded. diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-upper.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-upper.md deleted file mode 100644 index 3f6e435fffe..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-functions/oql-upper.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: "OQL UPPER" -url: /refguide/oql-upper/ ---- - -## Description - -The `UPPER` function converts all lowercase characters in a given string to uppercase and returns the result. - -## Syntax - -The syntax is as follows: - -```sql -UPPER ( expression ) -``` - -`expression` specifies the string to convert. diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-operators/_index.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-operators/_index.md deleted file mode 100644 index 6d9d3fdc478..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-operators/_index.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: "OQL Operators" -url: /refguide/oql-operators/ ---- - - -The following operators can be used in OQL expressions: - -| Operator | Description | Example | -| --- | --- | --- | -| `+` | Addition | `6 + 4` returns 10. | -| `-` | Subtraction | `6 - 4` returns 2. | -| `*` | Multiplication | `6 * 4` returns 24. | -| `:` | Division | `8 : 4` returns 2. | -| `%` | Modulo | `8 % 3` returns 2. | -| `=` | Equal to | `Price = 9.80` returns true if price is 9.80, false if price is 9.90. | -| `!=` | Not equal to | `Price != 9.80` returns true if price is 9.90, false if price is 9.80. | -| `<` | Less than | `Price < 9.80` returns true if price is 9.70, false if price is 9.80. | -| `<=` | Less than or equal to | `Price <= 9.80` returns true if price is 9.80, false if price is 9.90. | -| `>` | Greater than | `Price > 9.80` returns true if price is 9.90, false if price is 9.80. | -| `>=` | Greater than or equal to | `Price >= 9.80` returns true if price is 9.80, false if price is 9.70. | -| `LIKE` | Matches the pattern after the operator. The wildcard character '%' can be used to define any string of zero or more characters. In order to search for special characters like `%`, `_`, and `\`, they should be escaped with the `\` escape character. | `City LIKE '%dun'` returns all the cities with names that end with 'dun', like 'dun' and 'Losdun'.
`Symbol LIKE '%\%'` returns all the symbols that end with the `%` special character.| -| `IN` | Matches any value in a subquery or a list of expression values. | `City IN (SELECT Name FROM City WHERE Country = 'Gelre')` `City IN ('Losdun', 'Die Haghe', 'Haagambacht')` | -| `EXISTS` | Test for the existence of any rows when executing the subquery. | `EXISTS (SELECT ID FROM City WHERE City = 'Losdun')` Returns true if object exists | -| `NOT` | Reverses the value of the expression following this keyword. | `NOT City = 'Rotterdam'` returns all objects not in Rotterdam. | -| `CASE` | Evaluates one or more conditions and returns a possible expression. | See [OQL Case Expressions](/refguide/oql-case-expression/). | -| `OR` | Returns true if one or both expressions around this operator return true. | `price = 9.80 OR price = 9.70` returns true if price is 9.80, false if price is 9.60. | -| `AND` | Returns true if expressions on both sides return true. | `price = 9.80 AND amount = 1` returns true if price is 9.80 and amount is 1, false if price is 9.70 and amount is 1, false if price is 9.80 and amount is 2. | diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-operators/oql-case-expression.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-operators/oql-case-expression.md deleted file mode 100644 index 97ca47fd537..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-operators/oql-case-expression.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: "OQL Case Expression" -url: /refguide/oql-case-expression/ ---- - -## Description - -The `CASE` expression is a conditional expression, similar to if/else statements in other programming languages. Each condition is an expression that returns a Boolean result. If the condition's result is true, the value of the `CASE` expression is the result that follows the condition, and the remainder of the `CASE` expression is not processed. If the condition's result is not true, any subsequent `WHEN` clauses are examined in the same manner. If no `WHEN` condition yields true, the value of the `CASE` expression is the result of the `ELSE` clause. If the `ELSE` clause is omitted and no condition is true, the result is null. - -## Usage - -The `CASE` expression can be used in two ways – simple: - -```sql - CASE input_expression - WHEN when_expression THEN result_expression [ ...n ] - ELSE else_result_expression - END -``` - -or extended: - -```sql - CASE - WHEN boolean_expression THEN result_expression [ ...n ] - ELSE else_result_expression - END -``` - -## Syntax - -### input_expression - -`input_expression` will be compared to `when_expression`. If `input_expression` matches `when_expression`, the result of the whole `CASE` expression will be `result_expression` given after `THEN`. - -### when_expression - -`when_expression` will be compared to `input_expression`. When `input_expression` matches this `when_expression`, the result of the whole `CASE` expression will be `result_expression` given after `THEN`. - -### boolean_expression - -`boolean_expression` must result in a Boolean value. When this expression returns true, the result of the whole `CASE` expression will be `result_expression` given after `THEN`. - -### result_expression - -`result_expression` is the possible result of the whole `CASE` expression. - -### else_result_expression - -`else_result_expression` is the result of the whole `CASE` expression, when no `result_expression` is possible. diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-parameters.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-parameters.md deleted file mode 100644 index 7be46dd9017..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-parameters.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: "OQL Parameters" -url: /refguide/oql-parameters/ ---- - -## Introduction - -Currently, parameters are only supported within OQL queries defined in [datasets](/refguide/data-sets/). To use a defined parameter in a query, use the `$` sign. - -## Examples - -Examples of correct parameter names are `$weight_range`, `$age`. - -If a parameter value is not set in an OQL query, that part of the statement is ignored. For example, in the following query: - -```sql -SELECT Name -FROM Module.Person -WHERE - Age > $param - AND - Active = true -``` - -If the value of `$param` is not provided, the query will be equivalent to: - -```sql -SELECT Name -FROM Module.Person -WHERE - Active = true -``` - -The example above is different from the case where the value of `$param` is provided, but is `NULL`. In that case, the query will be equivalent to: - -```sql -SELECT Name -FROM Module.Person -WHERE - Age > NULL - AND - Active = true -``` diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-subqueries.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-subqueries.md deleted file mode 100644 index 396e874d508..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-expressions/oql-subqueries.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: "OQL Subqueries" -url: /refguide/oql-subqueries/ ---- - -## Description - -A subquery is a nested SELECT query inside certain clauses of a SELECT query. Subqueries can be used in a: - -* `SELECT` clause -* `FROM` clause -* `WHERE` clause -* `HAVING` clause combined with `GROUP BY` - -A subquery must always be placed in parentheses. - -## Subquery in SELECT - -Example: - -```sql -SELECT - City, - ZipCodeValue AS ZipCodeAlias, - (SELECT COUNT(*) FROM Module.Address WHERE Module.Address.ZipCode = ZipCodeEntity.ZipCodeValue) AS NumberOfAddresses -FROM - Module.ZipCode AS ZipCodeEntity -``` - -If a subquery is used in a `SELECT` clause, it should return exactly one column and at most one row. If it returns no rows, the value is replaced with `NULL`. - -The subquery can refer to attributes of other entities and subqueries in the main query's `FROM` clause. Those attributes can be referenced only by name, not by alias (In the example above, attribute `ZipCodeValue` has an alias in the main query, but it is referenced by name in the subquery). - -Other entities and subqueries in the main query's `FROM` clause can only be referenced by alias, if there is one. If no alias is set, they can be referenced by name (In the example above, entity `Module.ZipCode` has an alias in the main query, and that alias is used in the subquery). - -To avoid ambiguity, it is recommended to always refer to attributes with corresponding entity names in the format `..` if there is no alias or `.` if there is an alias. - -## Subquery in FROM - -Example: - -```sql -SELECT - ZipCode.City, - ZipCode.ZipCode, - AddressStats.AddressCount, - AddressStats.FirstHouseYear -FROM - Module.ZipCode AS ZipCode - INNER JOIN ( - SELECT - COUNT(*) AS AddressCount, - MIN(YearBuilt) AS FirstHouseYear, - ZipCode - FROM - Module.Address - GROUP BY ZipCode - ) AS AddressStats - ON ZipCode.ZipCode = AddressStats.ZipCode -``` - -A subquery can be used in the `FROM` clause of the main query in the same manner as an entity name. When used in a `FROM` clause, a subquery can return multiple columns and multiple rows. - -In contrast with a subquery in a `SELECT` clause, a subquery in a `FROM` clause cannot contain references to other entities and subqueries declared in the main query's `FROM` clause. - -## Subquery in WHERE - -There are two ways to use a subquery in a `WHERE` clause—as a value and as a collection. - -### Value Subqueries - -A value subquery is a subquery that returns exactly one row and exactly one column. In that case, its result can be considered a value. It can be used in a `WHERE` clause with the comparison operators `=`, `!=`, `<`, `<=`, `>`, `>=`. - -Example: - -```sql -SELECT - * -FROM - Module.House AS House -WHERE - House.Price < (SELECT AVG(Price) FROM Module.House) -``` - -### Collection Subqueries - -A collection subquery is a subquery that can have more than one row. If a subquery is used in a `WHERE` clause, it must always contain one column. Such a subquery can be referenced as a collection using the `IN` and `EXISTS` keywords. - -Examples: - -```sql -SELECT - * -FROM - Module.Venue AS Venue -WHERE - Venue.Name IN ( - SELECT - VenueName - FROM - Module.Event - WHERE - EventDate = '2025-04-01' - ) -``` - -```sql -SELECT - * -FROM - Module.Venue AS Venue -WHERE - EXISTS ( - SELECT - VenueName - FROM - Module.Event - WHERE - EventDate = '2025-04-01' - AND - VenueName = Venue.VenueName - ) -``` - -## Subquery in HAVING - -A subquery can also be used in a `HAVING` clause in a similar way to a `WHERE` clause. The difference is that only attributes and aggregates from the query can be referred to in the `HAVING` subquery. - -Example: - -```sql -SELECT - ZipCode, - AVG(Price) -FROM - Module.House AS House -GROUP BY - ZipCode -HAVING - AVG(Price) < (SELECT AVG(Price) FROM Module.House) -``` diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-from-clause.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-from-clause.md deleted file mode 100644 index 2ee77ae0502..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-from-clause.md +++ /dev/null @@ -1,135 +0,0 @@ ---- -title: "OQL From Clause" -url: /refguide/oql-from-clause/ -weight: 20 ---- - -## Description - -The `FROM` clause specifies the entities or other source from which the data must be retrieved. This clause starts with the `FROM` keyword, followed by an entity name. To select data from other entities, add these entities via the `JOIN` keywords. This syntax is a little more strict than the official `SQL FROM` clause syntax. - -## Syntax - -This is an example of the full syntax: - -```sql -FROM - { - entity_name | ( sub_oql_query ) - } - [ [ AS ] from_alias ] - - { - { INNER | { { LEFT | RIGHT | FULL } [ OUTER ] } } JOIN - entity_path | entity_name | ( sub_oql_query ) [ [ AS ] from_alias ] - [ ON ] - } [ ,...n ] -``` - -### entity_name - -`entity_name` specifies the entity from which data must be retrieved. The entity name can be optionally encapsulated in double quotes. If the entity name is a reserved OQL word (like `Order` or `Group`), double quotes are mandatory. For more information, see the [Reserved Words](/refguide/oql/#reserved-oql-words) section in *OQL*. - -### ( sub_oql_query ) - -`( sub_oql_query )` specifies another OQL query from which data must be retrieved. This will be the source for the current query. The subquery must be placed within parentheses. See [OQL Subqueries](/refguide/oql-subqueries/) for more details. - -### JOIN - -There are four different `JOIN` types supported: - -* `INNER JOIN` -* `LEFT OUTER JOIN` -* `RIGHT OUTER JOIN` -* `FULL JOIN` - -The syntax is as follows: - -```sql - { INNER | { { LEFT | RIGHT | FULL } [ OUTER ] } } JOIN - entity_path | entity_name | ( sub_oql_query ) [ [ AS ] from_alias ] - [ ON ] -``` - -#### entity_path - -`entity_path` specifies the entity to join and the path from an earlier defined entity in the `FROM` clause to this entity. With `entity-path` the `ON` condition is optional. - -The example path `Crm.Customer/Crm.Customer_Address/Crm.Address` defines a path from the entity **Crm.Customer** to a new entity **Crm.Address**. - -Similar to `entity_name`, double quotes can be used. - -#### entity_name | ( sub_oql_query ) - -`entity_name` or `( sub_oql_query )` can be used in a `JOIN` statement in the same way they can be used directly in `FROM`. In contrast to `entity_path`, an `ON` condition is required. - -#### \[ ON \ \] - -`[ ON ]` constrains the specified entity in the `JOIN` part of the `FROM` clause. The constraint syntax is similar to that of the `WHERE` clause. Only the entities and `FROM` aliases from the current and preceding `JOIN` elements can be used in the constraint. - -In the case of `entity_path`, using constraints is optional – the system will generate the appropriate `JOIN` condition based on the specified `entity_path`. - -In cases when an entity name or a subquery is used, the `JOIN` condition (i.e. `ON` constraint) is mandatory. - -#### JOIN Types - -##### INNER JOIN - -An `INNER JOIN` is the most common join operation between entities and represents the default join type. The query compares each row of entity A with each row of entity B to find all the pairs of rows that have an association and satisfy the `JOIN` predicate. If the association exists and the `JOIN` predicate is satisfied, the column values for each matched pair of rows of A and B are combined into a resulting row. - -The syntax is as follows: - -```sql -[ INNER ] JOIN entity_path [ ON ] -``` - -##### LEFT OUTER JOIN - -A `LEFT OUTER JOIN` query compares each row of entity A with each row of entity B to find all pairs of rows which have an association and thus satisfy the `JOIN` predicate. When the association exists and the `JOIN` predicate is satisfied, column values for each matched pair of rows of A and B are combined into a resulting row. - -However, in contrast to the `INNER JOIN` construction, the query will also return rows of entity A which do not match entity B. When columns of entity B are specified, these columns contain a null value for these rows. - -The syntax is as follows: - -```sql -LEFT [ OUTER ] JOIN entity_path [ ON ] -``` - -##### RIGHT OUTER JOIN - -A `RIGHT OUTER JOIN` query compares each row of entity A with each row of entity B to find all pairs of rows which have an association and thus satisfy the `JOIN` predicate. If the association exists and the `JOIN` predicate is satisfied, the column values for each matched pair of rows of A and B are combined into a resulting row. - -However, in contrast to the `INNER JOIN` construction, rows from entity B that do not match entity A will also be returned. When columns of entity A are specified, these columns contain a null value for these rows. - -The syntax is as follows: - -```sql -RIGHT [ OUTER ] JOIN entity_path [ ON ] -``` - -##### FULL OUTER JOIN - -A `FULL OUTER JOIN` query compares each row of entity A with each row of entity B to find all pairs of rows which have an association and thus satisfy the `JOIN` predicate. When the association exists and the `JOIN` predicate is satisfied, column values for each matched pair of rows from A and B are combined into a result row. - -However, in contrast to the `INNER JOIN` construction, data from entities that do *not* match will also be returned. For these rows, columns of missing entities will contain null values. - -The syntax is as follows: - -```sql -FULL [ OUTER ] JOIN entity_path [ ON ] -``` - -#### Example - -In this scenario, you are using a `LEFT OUTER JOIN` to get the records in table A that have no association in table B. - -For example, you have the entities **Customer** and **Order**, where a customer can have an association to multiple orders. You want to retrieve all the customers that have no orders at all. - -```sql -SELECT - Customer/Name as Name, - Customer/ as -FROM MyModule.Customer - LEFT OUTER JOIN Customer/MyModule.Customer_Order/MyModule."Order" as "Order" -WHERE "Order"/ID IS NULL -``` diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-group-by-clause.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-group-by-clause.md deleted file mode 100644 index 253884b4756..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-group-by-clause.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: "OQL Group by Clause" -url: /refguide/oql-group-by-clause/ -weight: 40 ---- - -## Description - -The `GROUP BY` clause will condense all returned rows into a single row that shares the same values for the expressions defined in this clause. The expressions in this clause must exist in the `SELECT` clause of the query. All expressions in the `SELECT` clause which do not exist in the `GROUP BY` clause must be either an aggregation or the result an aggregate function. - -## Syntax - -The syntax is as follows: - -```sql -GROUP BY - expression [ ,...n ] - -[HAVING ] -``` - -### expression - -`expression` specifies the expressions by which values of the rows are grouped. - -### HAVING \ - -`HAVING ` specifies a constraint that must be defined in a `HAVING` clause, when a `GROUP BY` expression is used. - -## Examples - -This query returns the count of all customers per city: - -```sql -SELECT COUNT(Sales.Customer/*) -FROM Sales.Customer -INNER JOIN Sales.Customer/Sales.Customer_Address/Sales.Address -GROUP BY Sales.Address/City -``` - -This query returns the sum of the total prices of all orders per city: - -```sql -SELECT SUM(Sales."Order"/TotalPrice) -FROM Sales."Order" -INNER JOIN Sales."Order"/Sales.Customer_Order/Sales.Customer/Sales.Customer_Address/Sales.Address -GROUP BY Sales.Address/City -``` - -This query returns the sum of the total prices of all orders per city for which the sum is greater than 1000.00 or the City is Losdun: - -```sql -SELECT SUM(Sales."Order"/TotalPrice) -FROM Sales."Order" -INNER JOIN Sales."Order"/Sales.Customer_Order/Sales.Customer/Sales.Customer_Address/Sales.Address -GROUP BY Sales.Address/City -HAVING SUM(Sales."Order"/TotalPrice) > 1000.0 OR Sales.Address/City = 'Losdun' -``` diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-limit-clause.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-limit-clause.md deleted file mode 100644 index b811dbff841..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-limit-clause.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: "OQL Limit Clause" -url: /refguide/oql-limit-clause/ -weight: 60 ---- - -## Description - -With the `LIMIT` clause a portion of the result of a query can be returned. - -## Syntax - -The syntax is as follows: - -```sql -[ LIMIT number ] [ OFFSET number ] -``` - -### LIMIT - -`LIMIT` specifies how many rows must be returned. - -### OFFSET - -`OFFSET` specifies how many rows must be skipped before returning the result rows. - -## Examples - -This query retrieves the first ten customers, sorted by their last name: - -```sql -SELECT FirstName FROM Sales.Customer -ORDER BY LastName -LIMIT 10 -``` - -This query retrieves all customers, except the first ten, sorted by their last name: - -```sql -SELECT FirstName FROM Sales.Customer -ORDER BY LastName -OFFSET 10 -``` - -This query retrieves the 11th to 20th customer, sorted by their last name: - -```sql -SELECT FirstName FROM Sales.Customer -ORDER BY LastName -LIMIT 10 OFFSET 10 -``` diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-order-by-clause.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-order-by-clause.md deleted file mode 100644 index dbb94d36783..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-order-by-clause.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: "OQL Order by Clause" -url: /refguide/oql-order-by-clause/ -weight: 50 ---- - -## Description - -The `ORDER BY` clause specifies the sort order used on columns returned in a `SELECT` statement. Multiple columns can be specified. Columns are ordered in the sequence of the items in the `ORDER BY` clause. - -This clause can include items that do not appear in the `SELECT` clause, except when `SELECT DISTINCT` is specified or when a `GROUP BY` clause exists. When `UNION` is used, the column names or aliases must be those specified in the `SELECT` clause of the first part of the query. - -## Syntax - -The syntax is as follows: - -```sql -ORDER BY - { - order_by_expression [ ASC | DESC ] - } -``` - -### order_by_expression - -`order_by_expression` specifies an attribute of an entity or an alias from the `FROM` clause to sort on. - -### ASC - -`ASC` specifies that the results must be ordered ascending, from the lowest to the highest value. This is the default sort type. - -### DESC - -`DESC` specifies that the results must be ordered descending, from the highest to the lowest value. - -{{% alert color="info" %}} -For details on the default ordering behavior of NULL values, see the [NULL Values Order Behavior](/refguide/ordering-behavior/#null-ordering-behavior) section of *Order By Behavior*. -{{% /alert %}} - -## Examples - -This query retrieves all customers and returns the first names sorted on the last name, ascending: - -```sql -SELECT FirstName FROM Sales.Customer -ORDER BY LastName -``` - -This query retrieves all customers and returns the first and last name sorted on the last name, descending: - -```sql -SELECT FirstName + ' ' + LastName FROM Sales.Customer -ORDER BY LastName DESC -``` diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-select-clause.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-select-clause.md deleted file mode 100644 index 986c76ba308..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-select-clause.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: "OQL Select Clause" -url: /refguide/oql-select-clause/ -weight: 10 ---- - -## Description - -The `SELECT` clause specifies which entity attributes or other specified data must be retrieved. The `SELECT` clause consists of the term `SELECT` and one or more expressions. These expressions must be separated by a comma. Each expression defines a column in the result. Each expression can have an alias, which will be the name of the column in the result. - -## Syntax - -The syntax is as follows: - -```sql -SELECT [ DISTINCT ] - { - * - | { entity_name | from_alias }.* - | { expression [ [ AS ] column_alias ] } [ ,...n ] - } -``` - -### DISTINCT - -`DISTINCT` specifies that double rows must not be shown in the result. - -### * (asterisk) - -`*` (asterisk) specifies that all attributes from all entities in the `FROM` clause should be returned. - -### entity_name.*and from_alias.* - -`entity_name.*` and `from_alias.*` specify that all attributes of the specified entity or expression of the `FROM` clause should be returned. `entity_name` can be optionally put in double quotes. If the entity name is a reserved OQL word (like `Order` or `Group`), double quotes are mandatory. For more information, see the [Reserved Words](/refguide/oql/#reserved-oql-words) section in *OQL*. - -```sql -SELECT Sales.Customer.* FROM Sales.Customer -``` - -```sql -SELECT Person.* FROM Sales.Customer AS Person -``` - -```sql -SELECT "Sales.Order".* FROM "Sales.Order" -``` - -### expression - -`expression` is either a constant, a function or any combination of attribute names, constants, and functions connected by operator (or operators) or a subquery. When you add more expressions, place a comma between each expression. - -```sql -SELECT Name AS CustomerName, LastName AS CustomerLastName, Birthday, Category FROM Sales.Customer -``` - -For more information, see [OQL Expressions](/refguide/oql-expressions/). - -### column_alias - -`column_alias` is an alternative name to replace the column name in the result. When the name attribute is retrieved, the result column is "Name". With an alias, you can specify another result column name, like "Customer_Name". An alias cannot contain spaces. - -```sql -SELECT Sales.Customer.Name AS CustomerName FROM Sales.Customer -``` - -```sql -SELECT Sales.Customer.Name AS "Customer_Name" FROM Sales.Customer -``` diff --git a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-where-clause.md b/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-where-clause.md deleted file mode 100644 index cc1eae85de4..00000000000 --- a/content/en/docs/refguide/modeling/resources/data-sets/oql/oql-where-clause.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: "OQL Where Clause" -url: /refguide/oql-where-clause/ -weight: 30 ---- - -## Description - -The `WHERE` clause specifies how the data being retrieved must be constrained. - -## Syntax - -The syntax is as follows: - -```sql -WHERE -``` - -`` is an expression for which the value always equals true. Expressions consist of simple comparisons using operators, functions, keywords or system variables. - -For more information, see [OQL Expressions](/refguide/oql-expressions/). - -## Examples - -This query retrieves all customers whose name is equal to "Jansen": - -```sql -SELECT FirstName FROM Sales.Customer -WHERE LastName = 'Jansen' -``` - -This query retrieves all customers who live in "Rotterdam": - -```sql -SELECT FirstName FROM Sales.Customer -INNER JOIN Sales.Customer/Sales.Customer_Address/Sales.Address -WHERE Sales.Address/City = 'Rotterdam' -```