Use the `SELECT` clause, along with `FROM`, `WHERE`, `GROUP BY`, `HAVING`, `ORDER BY`, and `LIMIT` to search and aggregate data.
Among these clauses, `SELECT` and `FROM` are required, as they specify which fields to retrieve and which indices to retrieve them from. All other clauses are optional. Use them according to your needs.
### Syntax
The complete syntax for searching and aggregating data is as follows:
The expression in turn can be combined into a predicate with logical operator. Use a predicate in the `WHERE` and `HAVING` clause to filter out data by specific conditions.
*Example 1*: Use index aliases to query across indexes. To learn about index aliases, see [Index Alias]({{site.url}}{{site.baseurl}}/opensearch/index-alias/).
`BETWEEN` | Similar to a range query. For more information about range queries, see [Range query]({{site.url}}{{site.baseurl}}/opensearch/query-dsl/term#range).
`LIKE` | Use for full text search. For more information about full-text queries, see [Full-text queries]({{site.url}}{{site.baseurl}}/opensearch/query-dsl/full-text/).
*Example 2*: OpenSearch allows for flexible schema, so documents in an index may have different fields. Use `IS NULL` or `IS NOT NULL` to retrieve only missing fields or existing fields. We do not differentiate between missing fields and fields explicitly set to `NULL`:
*Example 3*: Deletes a document that satisfies the predicates in the `WHERE` clause:
```sql
DELETE FROM accounts
WHERE age > 30
```
## Group By
Group documents with the same field value into buckets.
*Example 1*: Group by fields:
```sql
SELECT age
FROM accounts
GROUP BY age
```
| id | age
:--- | :---
0 | 28
1 | 32
2 | 33
3 | 36
*Example 2*: Group by field alias:
```sql
SELECT account_number AS num
FROM accounts
GROUP BY num
```
| id | num
:--- | :---
0 | 1
1 | 6
2 | 13
3 | 18
*Example 4*: Use scalar functions in the `GROUP BY` clause:
```sql
SELECT ABS(age) AS a
FROM accounts
GROUP BY ABS(age)
```
| id | a
:--- | :---
0 | 28.0
1 | 32.0
2 | 33.0
3 | 36.0
## Having
Use the `HAVING` clause to aggregate inside each bucket based on aggregation functions (`COUNT`, `AVG`, `SUM`, `MIN`, and `MAX`).
The `HAVING` clause filters results from the `GROUP BY` clause:
*Example 1*:
```sql
SELECT age, MAX(balance)
FROM accounts
GROUP BY age HAVING MIN(balance) > 10000
```
| id | age | MAX (balance)
:--- | :---
0 | 28 | 32838
1 | 32 | 39225
## Order By
Use the `ORDER BY` clause to sort results into your desired order.
*Example 1*: Use `ORDER BY` to sort by ascending or descending order. Besides regular field names, using `ordinal`, `alias`, or `scalar` functions are supported:
*Example 2*: Specify if documents with missing fields are to be put at the beginning or at the end of the results. The default behavior of OpenSearch is to return nulls or missing fields at the end. To push them before non-nulls, use the `IS NOT NULL` operator:
*Example 2*: If you pass in two arguments, the first is mapped to the `from` parameter and the second to the `size` parameter in OpenSearch. You can use this for simple pagination for small indices, as it's inefficient for large indices.
Use `ORDER BY` to ensure the same order between pages: