# RANGE QUERY

> 介绍了 Range 查询的语法和用法，包括 `FILL`、`TO` 和 `BY` 选项的详细说明和示例。

# RANGE QUERY

查询并聚合一个给定长度的时间范围的数据是时序数据常见的一种查询模式，例如 `PromQL` 中的 `Range selector`。而 GreptimeDB 在 SQL 中支持了 Range 查询，用于将时序数据汇总为时间块，并在时间块上进行数据的聚合。Range 查询作为 `SELECT` 语句的一部分，可与 SQL 灵活结合，从而在 SQL 中提供更强大的时序数据查询能力。

## Syntax

Range query 使用 `Time Index` 列作为聚合的时间线。
一个合法的 Range 查询语法结构如下所示：

```sql
SELECT
  AGGR_FUNCTION(column1, column2,..) RANGE INTERVAL [FILL FILL_OPTION],
  ...
FROM table_name
  [ WHERE <where_clause>]
ALIGN INTERVAL [ TO TO_OPTION ] [BY (columna, columnb,..)] [FILL FILL_OPTION]
  [ ORDER BY <order_by_clause>]
[ LIMIT <limit_clause>];

INTERVAL :=  TIME_INTERVAL | ( INTERVAL expr ) 
```

- 关键字 `ALIGN`，必选字段，后接参数 `INTERVAL` ，`ALIGN` 指明了 Range 查询的步长。
  - 子关键字 `TO` ，可选字段，指定 Range 查询对齐到的时间点，合法的 `TO_OPTION` 参数见[TO Option](#to-选项) 。
  - 子关键字 `BY` ，可选字段，后接参数 `(columna, columnb,..)` ，描述了聚合键。详情请见[BY OPTION](#by-选项)。
- 参数 `INTERVAL` ，主要用于给出一段时间长度，有两种参数形式：
  - 基于 `PromQL Time Durations` 格式的字符串（例如：`3h`、`1h30m`）。访问 [Prometheus 文档](https://prometheus.io/docs/prometheus/latest/querying/basics/#float-literals-and-time-durations) 获取该格式更详细的说明。
  - `Interval` 类型，使用 `Interval` 类型需要携带括号，（例如：`('1 year 3 hours 20 minutes'::INTERVAL)`）。访问 [Interval](./data-types.md#interval-type) 获取该格式更详细的说明。
- `AGGR_FUNCTION(column1, column2,..) RANGE INTERVAL [FILL FILL_OPTION]` 称为一个 Range 表达式。
  - `AGGR_FUNCTION(column1, column2,..)` 是一个聚合函数，代表需要聚合的表达式。
  - 关键字 `RANGE`，必选字段，后接参数 `INTERVAL` 指定了每次数据聚合的时间范围，
  - 关键字 `FILL`，可选字段，详情请见 [`FILL` Option](#fill-选项)。
  - Range 表达式可与其他运算结合，实现更复杂的查询。具体见[嵌套使用 Range 表达式](#嵌套使用-range-表达式) 。
- 关键字 `FILL`，可以跟在一个 Range 表达式后，详情请见[FILL Option](#fill-选项) 。

## `FILL` 选项

`FILL` 选项指定了在某个聚合的时间片上没有数据，或者聚合字段的值为空时的数据填充方法。

它可以跟在一个 Range 表达式后，作为这个 Range 表达式的数据填充方法；也可以放在 `ALIGN` 后面作为所有未指定 `FILL` 选项的 Range 表达式的填充方法。

例如，在下面的 SQL 代码中，
`max(val) RANGE '10s'` 范围表达式使用 `FILL` 选项 `LINEAR`，而 `min(val) RANGE '10s'` 没有指定 `FILL` 选项，它将使用在 `ALIGN`关键字之后指定的选项`PREV`。

```sql
SELECT 
    ts, 
    host, 
    min(val) RANGE '10s',
    max(val) RANGE '10s' FILL LINEAR 
FROM host_cpu 
ALIGN '5s' BY (host) FILL PREV;
```
`FILL` 有以下几种选项：

|   FILL   |                                                      描述                                                      |
| :------: | :------------------------------------------------------------------------------------------------------------: |
|  `NULL`  |                                        直接使用 `NULL` 填充                                        |
|  `PREV`  |                                             使用前一个点的数据填充                                             |
| `LINEAR` | 使用线性插值法填充数据，如果一个整数类型使用 `LINEAR` 填充，则该列的变量类型会在计算的时候被隐式转换为浮点类型 |
|   `X`    |                        填充一个常量，该常量的数据类型必须和 Range 表达式的变量类型一致                         |

以下面这张表为例

```sql
DROP TABLE IF EXISTS host_val;
CREATE TABLE host_val (
    ts TIMESTAMP TIME INDEX,
    host STRING,
    val DOUBLE,
    PRIMARY KEY (host)
);

INSERT INTO host_val VALUES
    ('1970-01-01 00:00:00', 'host1', 0),
    ('1970-01-01 00:00:15', 'host1', 6),
    ('1970-01-01 00:00:00', 'host2', 6),
    ('1970-01-01 00:00:15', 'host2', 12);
```

```sql
+---------------------+-------+------+
| ts                  | host  | val  |
+---------------------+-------+------+
| 1970-01-01 00:00:00 | host1 |    0 |
| 1970-01-01 00:00:15 | host1 |    6 |
| 1970-01-01 00:00:00 | host2 |    6 |
| 1970-01-01 00:00:15 | host2 |   12 |
+---------------------+-------+------+
```

不同 `FILL` 选项的结果如下：

<Tabs>

<TabItem value="NO FILL" label="NO FILL">

```sql
SELECT ts, host, min(val) RANGE '5s' FROM host_val ALIGN '5s';
```

```sql
+---------------------+-------+----------------------------+
| ts                  | host  | min(host_val.val) RANGE 5s |
+---------------------+-------+----------------------------+
| 1970-01-01 00:00:00 | host1 |                          0 |
| 1970-01-01 00:00:15 | host1 |                          6 |
| 1970-01-01 00:00:00 | host2 |                          6 |
| 1970-01-01 00:00:15 | host2 |                         12 |
+---------------------+-------+----------------------------+
```

</TabItem>

<TabItem value="FILL NULL" label="FILL NULL">

```sql
SELECT ts, host, min(val) RANGE '5s' FILL NULL FROM host_val ALIGN '5s';
```

```sql
+---------------------+-------+--------------------------------------+
| ts                  | host  | min(host_val.val) RANGE 5s FILL NULL |
+---------------------+-------+--------------------------------------+
| 1970-01-01 00:00:00 | host2 |                                    6 |
| 1970-01-01 00:00:05 | host2 |                                 NULL |
| 1970-01-01 00:00:10 | host2 |                                 NULL |
| 1970-01-01 00:00:15 | host2 |                                   12 |
| 1970-01-01 00:00:00 | host1 |                                    0 |
| 1970-01-01 00:00:05 | host1 |                                 NULL |
| 1970-01-01 00:00:10 | host1 |                                 NULL |
| 1970-01-01 00:00:15 | host1 |                                    6 |
+---------------------+-------+--------------------------------------+

```

</TabItem>

<TabItem value="FILL PREV" label="FILL PREV">

```sql
SELECT ts, host, min(val) RANGE '5s' FILL PREV FROM host_val ALIGN '5s';
```

```sql
+---------------------+-------+--------------------------------------+
| ts                  | host  | min(host_val.val) RANGE 5s FILL PREV |
+---------------------+-------+--------------------------------------+
| 1970-01-01 00:00:00 | host1 |                                    0 |
| 1970-01-01 00:00:05 | host1 |                                    0 |
| 1970-01-01 00:00:10 | host1 |                                    0 |
| 1970-01-01 00:00:15 | host1 |                                    6 |
| 1970-01-01 00:00:00 | host2 |                                    6 |
| 1970-01-01 00:00:05 | host2 |                                    6 |
| 1970-01-01 00:00:10 | host2 |                                    6 |
| 1970-01-01 00:00:15 | host2 |                                   12 |
+---------------------+-------+--------------------------------------+
```

</TabItem>

<TabItem value="FILL LINEAR" label="FILL LINEAR">

```sql
SELECT ts, host, min(val) RANGE '5s' FILL LINEAR FROM host_val ALIGN '5s';
```

```sql
+---------------------+-------+----------------------------------------+
| ts                  | host  | min(host_val.val) RANGE 5s FILL LINEAR |
+---------------------+-------+----------------------------------------+
| 1970-01-01 00:00:00 | host2 |                                      6 |
| 1970-01-01 00:00:05 | host2 |                                      8 |
| 1970-01-01 00:00:10 | host2 |                                     10 |
| 1970-01-01 00:00:15 | host2 |                                     12 |
| 1970-01-01 00:00:00 | host1 |                                      0 |
| 1970-01-01 00:00:05 | host1 |                                      2 |
| 1970-01-01 00:00:10 | host1 |                                      4 |
| 1970-01-01 00:00:15 | host1 |                                      6 |
+---------------------+-------+----------------------------------------+
```

</TabItem>

<TabItem value="FILL Constant Value 6.0" label="FILL Constant Value 6.0">

```sql [FILL Constant Value 6.0]
SELECT ts, host, min(val) RANGE '5s' FILL 6 FROM host_val ALIGN '5s';
```

```sql
+---------------------+-------+-----------------------------------+
| ts                  | host  | min(host_val.val) RANGE 5s FILL 6 |
+---------------------+-------+-----------------------------------+
| 1970-01-01 00:00:00 | host2 |                                 6 |
| 1970-01-01 00:00:05 | host2 |                                 6 |
| 1970-01-01 00:00:10 | host2 |                                 6 |
| 1970-01-01 00:00:15 | host2 |                                12 |
| 1970-01-01 00:00:00 | host1 |                                 0 |
| 1970-01-01 00:00:05 | host1 |                                 6 |
| 1970-01-01 00:00:10 | host1 |                                 6 |
| 1970-01-01 00:00:15 | host1 |                                 6 |
+---------------------+-------+-----------------------------------+
```

</TabItem>

</Tabs>

注意，如果存在多个 Range 表达式，只对其中的一个表达式使用了 FILL 方法的话，为了保持 SQL 输出行数的统一，其他 Range 表达式会被使用 FILL NULL 方法来填充缺失的时间片段。
所以下面两句 SQL 在输出上是等价的：

```sql
SELECT 
    ts, 
    host, 
    min(val) RANGE '10s',
    max(val) RANGE '10s' FILL LINEAR 
FROM host_val 
ALIGN '5s';
```

```sql
SELECT 
    ts, 
    host, 
    min(val) RANGE '10s' FILL NULL,
    max(val) RANGE '10s' FILL LINEAR 
FROM host_val 
ALIGN '5s';
```

上述 SQL 的结果如下：

```sql
+---------------------+-------+---------------------------------------+-----------------------------------------+
| ts                  | host  | min(host_val.val) RANGE 10s FILL NULL | max(host_val.val) RANGE 10s FILL LINEAR |
+---------------------+-------+---------------------------------------+-----------------------------------------+
| 1969-12-31 23:59:55 | host1 |                                     0 |                                       0 |
| 1970-01-01 00:00:00 | host1 |                                     0 |                                       0 |
| 1970-01-01 00:00:05 | host1 |                                  NULL |                      2.9999999999999996 |
| 1970-01-01 00:00:10 | host1 |                                     6 |                                       6 |
| 1970-01-01 00:00:15 | host1 |                                     6 |                                       6 |
| 1969-12-31 23:59:55 | host2 |                                     6 |                                       6 |
| 1970-01-01 00:00:00 | host2 |                                     6 |                                       6 |
| 1970-01-01 00:00:05 | host2 |                                  NULL |                                       9 |
| 1970-01-01 00:00:10 | host2 |                                    12 |                                      12 |
| 1970-01-01 00:00:15 | host2 |                                    12 |                                      12 |
+---------------------+-------+---------------------------------------+-----------------------------------------+
```

## `TO` 选项

`TO` 选项的值用于组确定范围查询的初始时间点。
`TO` 选项、`RANGE` 选项和 `ALIGN INTERVAL` 共同决定了范围查询的时间窗口。
请参考[时间范围窗口](/user-guide/query-data/sql.md#时间范围窗口)。

`TO` 选项的默认值为当前查询客户端的时区。如果想要设置时区，请参考 [MySQL 客户端](/user-guide/protocols/mysql.md#时区) 或 [PostgreSQL 客户端](/user-guide/protocols/postgresql.md#时区)文档中的时区设置。其他可用的 `TO` 选项有：

|     TO      |                                描述                                |
| :---------: | :----------------------------------------------------------------: |
|    `NOW`    |                         对齐到当前查询时间                         |
| `Timestamp` | 对齐到一个用户指定的时间戳上，支持时间戳格式 `RFC3339` / `ISO8601` |

假设我们有一个名为 `host_val` 的表有下面这些数据：

```sql
DROP TABLE IF EXISTS host_val;
CREATE TABLE host_val (
    ts TIMESTAMP TIME INDEX,
    host STRING,
    val DOUBLE,
    PRIMARY KEY (host)
);

INSERT INTO host_val VALUES
    ('2023-01-01 23:00:00', 'host1', 0),
    ('2023-01-02 01:00:00', 'host1', 1),
    ('2023-01-01 23:00:00', 'host2', 2),
    ('2023-01-02 01:00:00', 'host2', 3);
```

```sql
+---------------------+-------+------+
| ts                  | host  | val  |
+---------------------+-------+------+
| 2023-01-01 23:00:00 | host1 |    0 |
| 2023-01-02 01:00:00 | host1 |    1 |
| 2023-01-01 23:00:00 | host2 |    2 |
| 2023-01-02 01:00:00 | host2 |    3 |
+---------------------+-------+------+
```

对不同的 `TO` 选项的查询结果如下：

<Tabs>

<TabItem value="Default to timezone" label="Default to timezone">

```sql
-- 使用 mysql 协议查询数据库时区，当前处于 UTC 时区
SELECT @@time_zone;
```

```sql
+-------------+
| @@time_zone |
+-------------+
| UTC         |
+-------------+
```

```sql
-- 如果没有指定 `TO` 选项
-- 会使用当前查询指定的时区作为初始的对齐时间
SELECT ts, host, min(val) RANGE '1d' FROM host_val ALIGN '1d';
```

```sql
+---------------------+-------+----------------------------+
| ts                  | host  | min(host_val.val) RANGE 1d |
+---------------------+-------+----------------------------+
| 2023-01-01 00:00:00 | host1 |                          0 |
| 2023-01-02 00:00:00 | host1 |                          1 |
| 2023-01-01 00:00:00 | host2 |                          2 |
| 2023-01-02 00:00:00 | host2 |                          3 |
+---------------------+-------+----------------------------+
```

</TabItem>

<TabItem value="NOW" label="NOW">

```sql
-- 如果你想要将查询范围的初始时间对齐到当前时间，
-- 可以使用 `NOW` 关键字。
-- 假如当前的时间为 `2023-01-02T09:16:40.503000`。
SELECT ts, host, min(val) RANGE '1d' FROM host_val ALIGN '1d' TO NOW;
```

```sql
+----------------------------+-------+----------------------------+
| ts                         | host  | min(host_val.val) RANGE 1d |
+----------------------------+-------+----------------------------+
| 2023-01-01 09:54:55.456000 | host1 |                          0 |
| 2023-01-01 09:54:55.456000 | host2 |                          2 |
+----------------------------+-------+----------------------------+

```

</TabItem>

<TabItem value="Specific Timestamp" label="Specific Timestamp">

```sql
-- 如果你想要将查询范围的初始时间对其到特定的时间戳，
-- 例如北京时间 2023 年 12 月 1 日，
-- 你可以将 `TO` 选项的值设定为特定的时间戳 '2023-01-01T00:00:00+08:00'。
SELECT ts, host, min(val) RANGE '1d' FROM host_val ALIGN '1d' TO '2023-01-01T00:00:00+08:00';
```

```sql
+---------------------+-------+----------------------------+
| ts                  | host  | min(host_val.val) RANGE 1d |
+---------------------+-------+----------------------------+
| 2023-01-01 16:00:00 | host1 |                          0 |
| 2023-01-01 16:00:00 | host2 |                          2 |
+---------------------+-------+----------------------------+
```

</TabItem>

</Tabs>

如果要查询特定时间范围内的数据，也可以使用 `TO` 关键字指定时间戳达到目的。
例如，要查询 `val` 在 `00:45` 和 `06:45` 之间的每日最小值，
你可以使用 `2023-01-01T00:45:00` 作为 `TO` 选项以及指定 `6h` 的查询范围。

```sql
SELECT ts, host, min(val) RANGE '6h' FROM host_val ALIGN '1d' TO '2023-01-01T00:45:00';
```

```sql
+---------------------+-------+----------------------------+
| ts                  | host  | min(host_val.val) RANGE 6h |
+---------------------+-------+----------------------------+
| 2023-01-02 00:45:00 | host2 |                          3 |
| 2023-01-02 00:45:00 | host1 |                          1 |
+---------------------+-------+----------------------------+
```

## `BY` 选项

`BY` 选项描述聚合键。如果不指定该字段，则默认使用表的主键作为聚合键。如果表没有指定主键，则不能省略 `BY` 关键字。

假设我们有一个名为 `host_val` 的表有以下数据：

```sql
DROP TABLE IF EXISTS host_val;
CREATE TABLE host_val (
    ts TIMESTAMP TIME INDEX,
    host STRING,
    val DOUBLE,
    PRIMARY KEY (host)
);

INSERT INTO host_val VALUES
    ('2023-01-01 23:00:00', 'host1', 0),
    ('2023-01-02 01:00:00', 'host1', 1),
    ('2023-01-01 23:00:00', 'host2', 2),
    ('2023-01-02 01:00:00', 'host2', 3);
```

```sql
+---------------------+-------+------+
| ts                  | host  | val  |
+---------------------+-------+------+
| 2023-01-01 23:00:00 | host1 |    0 |
| 2023-01-02 01:00:00 | host1 |    1 |
| 2023-01-01 23:00:00 | host2 |    2 |
| 2023-01-02 01:00:00 | host2 |    3 |
+---------------------+-------+------+
```

下面的 SQL 使用 `host` 作为聚合键：

```sql
SELECT 
    ts, 
    host, 
    min(val) RANGE '10s' 
FROM host_val ALIGN '5s' BY (host);
```

```sql
+---------------------+-------+-----------------------------+
| ts                  | host  | min(host_val.val) RANGE 10s |
+---------------------+-------+-----------------------------+
| 2023-01-01 22:59:55 | host1 |                           0 |
| 2023-01-01 23:00:00 | host1 |                           0 |
| 2023-01-02 00:59:55 | host1 |                           1 |
| 2023-01-02 01:00:00 | host1 |                           1 |
| 2023-01-01 22:59:55 | host2 |                           2 |
| 2023-01-01 23:00:00 | host2 |                           2 |
| 2023-01-02 00:59:55 | host2 |                           3 |
| 2023-01-02 01:00:00 | host2 |                           3 |
+---------------------+-------+-----------------------------+
```

你还可以使用 `BY` 关键字

你还可以使用 `BY` 关键字声明其他列作为数据聚合的依据。比如下面这个 RANGE 查询，使用 `host` 列的字符串长度 `length(host)` 作为数据聚合的依据。

```sql
SELECT 
    ts, 
    length(host), 
    min(val) RANGE '10s' 
FROM host_val ALIGN '5s' BY (length(host));
```

得到的结果如下：

```sql
+---------------------+---------------------------------+-----------------------------+
| ts                  | character_length(host_val.host) | min(host_val.val) RANGE 10s |
+---------------------+---------------------------------+-----------------------------+
| 2023-01-01 22:59:55 |                               5 |                           0 |
| 2023-01-01 23:00:00 |                               5 |                           0 |
| 2023-01-02 00:59:55 |                               5 |                           1 |
| 2023-01-02 01:00:00 |                               5 |                           1 |
+---------------------+---------------------------------+-----------------------------+
```

你也可以显式通过 `BY ()` 声明不需要使用聚合键，将所有数据全部聚合到一个 group 里。**但如果直接将 `BY` 关键字省略，则代表着使用数据表的主键来作为数据的聚合键。**

```sql
SELECT
     ts,
     min(val) RANGE '10s'
FROM host_val ALIGN '5s' BY ();
```

得到的结果如下：

```sql
+---------------------+-----------------------------+
| ts                  | min(host_val.val) RANGE 10s |
+---------------------+-----------------------------+
| 2023-01-01 22:59:55 |                           0 |
| 2023-01-01 23:00:00 |                           0 |
| 2023-01-02 00:59:55 |                           1 |
| 2023-01-02 01:00:00 |                           1 |
+---------------------+-----------------------------+
```

## 聚合函数中的 `ORDER BY` 选项

Range 查询支持在聚合函数 `first_value` 和 `last_value` 中使用 `order by` 表达式，默认情况下，聚合函数使用时间索引列升序排列数据。

以该数据表为例：

```sql
DROP TABLE IF EXISTS host_val;
CREATE TABLE host_val (
    ts TIMESTAMP TIME INDEX,
    host STRING,
    val DOUBLE,
    addon DOUBLE,
    PRIMARY KEY (host)
);

INSERT INTO host_val VALUES
    ('1970-01-01 00:00:00', 'host1', 0, 3),
    ('1970-01-01 00:00:01', 'host1', 1, 2),
    ('1970-01-01 00:00:02', 'host1', 2, 1);
```

```sql
+---------------------+-------+------+-------+
| ts                  | host  | val  | addon |
+---------------------+-------+------+-------+
| 1970-01-01 00:00:00 | host1 |    0 |     3 |
| 1970-01-01 00:00:01 | host1 |    1 |     2 |
| 1970-01-01 00:00:02 | host1 |    2 |     1 |
+---------------------+-------+------+-------+
```

如果不指定 `order by` 表达式，默认使用时间索引 `ts` 列升序排列。
以下两个 SQL 是等价的：

```sql
SELECT ts, first_value(val) RANGE '5s', last_value(val) RANGE '5s' FROM host_val ALIGN '5s';
```

```sql
SELECT ts, first_value(val order by ts ASC) RANGE '5s', last_value(val order by ts ASC) RANGE '5s' FROM host_val ALIGN '5s';
```

查询后得到

```sql
+---------------------+------------------------------------+-----------------------------------+
| ts                  | first_value(host_val.val) RANGE 5s | last_value(host_val.val) RANGE 5s |
+---------------------+------------------------------------+-----------------------------------+
| 1970-01-01 00:00:00 |                                  0 |                                 2 |
+---------------------+------------------------------------+-----------------------------------+
```

也可以自定义排序规则，比如使用 `addon` 排序：

```sql
SELECT ts, first_value(val ORDER BY addon ASC) RANGE '5s', last_value(val ORDER BY addon ASC) RANGE '5s' FROM host_val ALIGN '5s';
```

查询后得到

```sql
+---------------------+-----------------------------------------------------------------------------+----------------------------------------------------------------------------+
| ts                  | first_value(host_val.val) ORDER BY [host_val.addon ASC NULLS LAST] RANGE 5s | last_value(host_val.val) ORDER BY [host_val.addon ASC NULLS LAST] RANGE 5s |
+---------------------+-----------------------------------------------------------------------------+----------------------------------------------------------------------------+
| 1970-01-01 00:00:00 |                                                                           2 |                                                                          0 |
+---------------------+-----------------------------------------------------------------------------+----------------------------------------------------------------------------+
```

## 嵌套使用 Range 表达式

Range 表达式支持灵活的嵌套，可以将 Range 表达式结合各种运算，提供更强大的查询能力。

以下面这张表为例：

```sql
DROP TABLE IF EXISTS host_val;
CREATE TABLE host_val (
    ts TIMESTAMP TIME INDEX,
    host STRING,
    val DOUBLE,
    PRIMARY KEY (host)
);

INSERT INTO host_val VALUES
    ('2023-01-01 08:00:00', 'host1', 1.1),
    ('2023-01-01 08:00:05', 'host1', 2.2),
    ('2023-01-01 08:00:00', 'host2', 3.3),
    ('2023-01-01 08:00:05', 'host2', 4.4);
```

```sql
+---------------------+-------+------+
| ts                  | host  | val  |
+---------------------+-------+------+
| 2023-01-01 08:00:00 | host1 |  1.1 |
| 2023-01-01 08:00:05 | host1 |  2.2 |
| 2023-01-01 08:00:00 | host2 |  3.3 |
| 2023-01-01 08:00:05 | host2 |  4.4 |
+---------------------+-------+------+
```

1. 聚合函数内部和外部都支持计算：

```sql
SELECT ts, host, 2.0 * min(val * 2.0) RANGE '10s' FROM host_val ALIGN '5s';
```

运行后得到

```sql
+---------------------+-------+-------------------------------------------------------+
| ts                  | host  | Float64(2) * min(host_val.val * Float64(2)) RANGE 10s |
+---------------------+-------+-------------------------------------------------------+
| 2023-01-01 07:59:55 | host1 |                                                   4.4 |
| 2023-01-01 08:00:00 | host1 |                                                   4.4 |
| 2023-01-01 08:00:05 | host1 |                                                   8.8 |
| 2023-01-01 07:59:55 | host2 |                                                  13.2 |
| 2023-01-01 08:00:00 | host2 |                                                  13.2 |
| 2023-01-01 08:00:05 | host2 |                                                  17.6 |
+---------------------+-------+-------------------------------------------------------+
```

2. 聚合函数内部和外部都支持使用 Scalar 函数：
   - `min(round(val)) RANGE '10s'` 表示对每个值先使用 `round` 函数四舍五入后再进行聚合
   - `round(min(val) RANGE '10s')` 表示对每个聚合完成的结果使用 `round` 函数四舍五入

```sql
SELECT ts, host, min(round(val)) RANGE '10s' FROM host_val ALIGN '5s';
```
运行后得到

```sql
+---------------------+-------+------------------------------------+
| ts                  | host  | min(round(host_val.val)) RANGE 10s |
+---------------------+-------+------------------------------------+
| 2023-01-01 07:59:55 | host1 |                                  1 |
| 2023-01-01 08:00:00 | host1 |                                  1 |
| 2023-01-01 08:00:05 | host1 |                                  2 |
| 2023-01-01 07:59:55 | host2 |                                  3 |
| 2023-01-01 08:00:00 | host2 |                                  3 |
| 2023-01-01 08:00:05 | host2 |                                  4 |
+---------------------+-------+------------------------------------+
```

```sql
SELECT ts, host, round(min(val) RANGE '10s') FROM host_val ALIGN '5s';
```

运行后得到

```sql
+---------------------+-------+------------------------------------+
| ts                  | host  | round(min(host_val.val) RANGE 10s) |
+---------------------+-------+------------------------------------+
| 2023-01-01 07:59:55 | host2 |                                  3 |
| 2023-01-01 08:00:00 | host2 |                                  3 |
| 2023-01-01 08:00:05 | host2 |                                  4 |
| 2023-01-01 07:59:55 | host1 |                                  1 |
| 2023-01-01 08:00:00 | host1 |                                  1 |
| 2023-01-01 08:00:05 | host1 |                                  2 |
+---------------------+-------+------------------------------------+
```

3. 多个 Range 表达式也可以相互计算，并且 Range 表达式支持分配律，下面两个表达式都是合法且等价的：

```sql
SELECT ts, host, max(val) RANGE '10s' - min(val) RANGE '10s' FROM host_val ALIGN '5s';
```

```sql
SELECT ts, host, (max(val) - min(val)) RANGE '10s' FROM host_val ALIGN '5s';
```

运行后得到

```sql
+---------------------+-------+-----------------------------------------------------------+
| ts                  | host  | max(host_val.val) RANGE 10s - min(host_val.val) RANGE 10s |
+---------------------+-------+-----------------------------------------------------------+
| 2023-01-01 07:59:55 | host2 |                                                         0 |
| 2023-01-01 08:00:00 | host2 |                                        1.1000000000000005 |
| 2023-01-01 08:00:05 | host2 |                                                         0 |
| 2023-01-01 07:59:55 | host1 |                                                         0 |
| 2023-01-01 08:00:00 | host1 |                                                       1.1 |
| 2023-01-01 08:00:05 | host1 |                                                         0 |
+---------------------+-------+-----------------------------------------------------------+
```

但注意，Range 表达式修饰的范围是位于 `RANGE` 关键字的前一个表达式，下面的 Range 查询是不合法的，因为 `RANGE` 关键字修饰的是表达式 `2.0`，并不是表达式 `min(val * 2.0) * 2.0`

```sql
SELECT ts, host, min(val * 2.0) * 2.0 RANGE '10s' FROM host_val ALIGN '5s';

ERROR 1815 (HY000): sql parser error: Can't use the RANGE keyword in Expr 2.0 without function
```

可以为表达式加上括号，`RANGE` 关键字会自动应用到括号中包含的所有聚合函数：

```sql
SELECT ts, host, (min(val * 2.0) * 2.0) RANGE '10s' FROM host_val ALIGN '5s';
```

运行后得到：

```sql
+---------------------+-------+-------------------------------------------------------+
| ts                  | host  | min(host_val.val * Float64(2)) RANGE 10s * Float64(2) |
+---------------------+-------+-------------------------------------------------------+
| 2023-01-01 07:59:55 | host2 |                                                  13.2 |
| 2023-01-01 08:00:00 | host2 |                                                  13.2 |
| 2023-01-01 08:00:05 | host2 |                                                  17.6 |
| 2023-01-01 07:59:55 | host1 |                                                   4.4 |
| 2023-01-01 08:00:00 | host1 |                                                   4.4 |
| 2023-01-01 08:00:05 | host1 |                                                   8.8 |
+---------------------+-------+-------------------------------------------------------+
```

Range 表达式不允许嵌套，嵌套的 Range 查询是不合法的：

```sql
SELECT ts, host, max(min(val) RANGE '10s') RANGE '10s' FROM host_cpu ALIGN '5s';

ERROR 1815 (HY000): Range Query: Nest Range Query is not allowed
```
