Skip to content

Commit

Permalink
docs(sql): update SQL syntax for WINDOW and JOIN (#3555)
Browse files Browse the repository at this point in the history
New SQLs:
- WINDOW without order by 
- LEFT JOIN
- LAST JOIN ( LAST JOIN )
- LAST JOIN ( WINDOW )
  • Loading branch information
aceforeverd committed Nov 20, 2023
1 parent 4433970 commit 1c153e0
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ OpenMLDB仅支持上线[SELECT查询语句](../dql/SELECT_STATEMENT.md)。

下表列出了在线请求模式支持的 `SELECT` 子句。

| SELECT 子句 | 说明 |
|:-------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|
| 单张表的简单表达式计算 | 简单的单表查询是对一张表进行列运算、使用运算表达式或单行处理函数(Scalar Function)以及它们的组合表达式作计算。需要遵循[在线请求模式下单表查询的使用规范](#在线请求模式下单表查询的使用规范) |
| [`JOIN` 子句](../dql/JOIN_CLAUSE.md) | OpenMLDB目前仅支持**LAST JOIN**。需要遵循[在线请求模式下LAST JOIN的使用规范](#在线请求模式下-last-join-的使用规范) |
| SELECT 子句 | 说明 |
| :--------------------------------------- | :----------------------------------------------------------- |
| 单张表的简单表达式计算 | 简单的单表查询是对一张表进行列运算、使用运算表达式或单行处理函数(Scalar Function)以及它们的组合表达式作计算。需要遵循[在线请求模式下单表查询的使用规范](#在线请求模式下单表查询的使用规范) |
| [`JOIN` 子句](../dql/JOIN_CLAUSE.md) | OpenMLDB目前仅支持**LAST JOIN**。需要遵循[在线请求模式下LAST JOIN的使用规范](#在线请求模式下-last-join-的使用规范) |
| [`WINDOW` 子句](../dql/WINDOW_CLAUSE.md) | 窗口子句用于定义一个或者若干个窗口。窗口可以是有名或者匿名的。用户可以在窗口上调用聚合函数进行分析计算。需要遵循[在线请求模式下Window的使用规范](#在线请求模式下window的使用规范) |

## 在线请求模式下 `SELECT` 子句的使用规范
Expand Down Expand Up @@ -57,15 +57,19 @@ SELECT substr(COL7, 3, 6) FROM t1;

### 在线请求模式下 `LAST JOIN` 的使用规范

- 仅支持`LAST JOIN`类型。
- 至少有一个JOIN条件是形如`left_source.column=right_source.column`的EQUAL条件,**并且`right_source.column`列需要命中右表的索引(key 列)**
- 带排序LAST JOIN的情况下,`ORDER BY`只支持单列的列引用表达式,列类型为 int16, int32, int64 or timestamp, **并且列需要命中右表索引的时间列**
- 右表 TableRef
1. 仅支持`LAST JOIN`类型。
2. 至少有一个JOIN条件是形如`left_source.column=right_source.column`的EQUAL条件,**并且`right_source.column`列需要命中右表的索引(key 列)**
3. 带排序LAST JOIN的情况下,`ORDER BY`只支持单列的列引用表达式,列类型为 int64 timestamp, **并且列需要命中右表索引的时间列**满足条件 2 和 3 的情况我们简单称做表能被 LAST JOIN 的 JOIN 条件优化
4. 右表 TableRef
- 可以指一张物理表, 或者子查询语句
- 子查询情况, 只支持
- 子查询情况, 目前支持
- 简单列筛选 (`select * from tb` or `select id, val from tb`)
- 窗口聚合子查询, 例如 `select id, count(val) over w as cnt from t1 window w as (...)`. 这种情况下, 子查询和 last join 的左表必须有相同的主表, 主表指计划树下最左边的物理表节点.
- **Since OpenMLDB 0.8.0** 带 WHERE 条件过滤的简单列筛选 ( 例如 `select * from tb where id > 10`)
- 窗口聚合子查询, 例如 `select id, count(val) over w as cnt from t1 window w as (...)`.
- OpenMLDB 0.8.4 之前, 如果 LAST JOIN 的右表是窗口聚合子查询, 需要和 LAST JOIN 的左表输入有相同的主表
- [ALPHA] OpenMLDB >= 0.8.4, 允许 LAST JOIN 下的窗口聚合子查询不带主表. 详细见下面的例子
- **OpenMLDB >= 0.8.0** 带 WHERE 条件过滤的简单列筛选 ( 例如 `select * from tb where id > 10`)
- **[ALPHA] OpenMLDB >= 0.8.4** 右表是带 LAST JOIN 的子查询 `subquery`, 要求 `subquery` 最左的表能被 JOIN 条件优化, `subquery`剩余表能被自身 LAST JOIN 的 JOIN 条件优化
- **[ALPHA] OpenMLDB >= 0.8.4** LEFT JOIN. 要求 LEFT JOIN 的右表能被 LEFT JOIN 条件优化, LEFT JOIN 的左表能被上层的 LAST JOIN 条件优化

**Example: 支持上线的 `LAST JOIN` 语句范例**
创建两张表以供后续`LAST JOIN`
Expand Down Expand Up @@ -115,15 +119,82 @@ desc t1;
t1.col0 as t1_col0,
t1.col1 + t2.col1 + 1 as test_col1,
FROM t1
LAST JOIN t2 ORDER BY t2.std_time ON t1.col1=t2.col1;
LAST JOIN t2 ORDER BY t2.std_time ON t1.col1=t2.col1;
```

右表是带 LAST JOIN 或者 WHERE 条件过滤的情况

```sql
CREATE TABLE t3 (col0 STRING, col1 int, std_time TIMESTAMP, INDEX(KEY=col1, TS=std_time, TTL_TYPE=absolute, TTL=30d));
-- SUCCEED

SELECT
t1.col1 as t1_col1,
t2.col1 as t2_col1,
t2.col0 as t2_col0
FROM t1 LAST JOIN (
SELECT * FROM t2 WHERE strlen(col0) > 0
) t2
ON t1.col1 = t2.col1

-- t2 被 JOIN 条件 't1.col1 = tx.t2_co1l' 优化, t3 被 JOIN 条件 't2.col1 = t3.col1'
SELECT
t1.col1 as t1_col1,
tx.t2_col1,
tx.t3_col1
FROM t1 LAST JOIN (
SELECT t2.col1 as t2_col1, t3.col1 as t3_col1
FROM t2 LAST JOIN t3
ON t2.col1 = t3.col1
) tx
ON t1.col1 = tx.t2_col1

-- 右表是 LEFT JOIN
SELECT
t1.col1 as t1_col1,
tx.t2_col1,
tx.t3_col1
FROM t1 LAST JOIN (
SELECT t2.col1 as t2_col1, t3.col1 as t3_col1
FROM t2 LEFT JOIN t3
ON t2.col1 = t3.col1
) tx
ON t1.col1 = tx.t2_col1

-- OpenMLDB 0.8.4 之前, LAST JOIN 窗口子查询需要窗口的子查询主表和当前主表一致
-- 这里都是 t1
SELECT
t1.col1,
tx.agg
FROM t1 LAST JOIN (
SELECT col1, count(col2) over w as agg
FROM t1 WINDOW w AS (
UNION t2
PARTITION BY col2 order by std_time ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
INSTANCE_NOT_IN_WINDOW EXCLUDE CURRENT_ROW
)
)

-- 右表是窗口聚合计算
-- OpenMLDB >= 0.8.4, 允许 t1 LAST JOIN WINDOW (t2). t1 是主表, t2 是一张副表
-- 此 SQL 和上一个例子语义一致
SELECT
t1.col1,
tx.agg
FROM t1 LAST JOIN (
SELECT col1, count(col2) over w as agg
FROM t2 WINDOW w AS (PARTITION BY col2 order by std_time ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
)
```



### 在线请求模式下Window的使用规范

- 窗口边界仅支持`PRECEDING``CURRENT ROW`
- 窗口类型仅支持`ROWS``ROWS_RANGE`
- 窗口`PARTITION BY`只支持列表达式,可以是多列,并且所有列需要命中索引,主表和 union source 的表都需要符合要求
- 窗口`ORDER BY`只支持列表达式,只能是单列,并且列需要命中索引的时间列,主表和 union source 的表都需要符合要求
- 窗口`ORDER BY`只支持列表达式,只能是单列,并且列需要命中索引的时间列,主表和 union source 的表都需要符合要求. 从 OpenMLDB 0.8.4 开始, ORDER BY 可以不写, 但需要满足额外的要求, 详见 [WINDOW CLAUSE](../dql/WINDOW_CLAUSE.md)
- 可支持使用 `EXCLUDE CURRENT_ROW``EXCLUDE CURRENT_TIME``MAXSIZE``INSTANCE_NOT_IN_WINDOW`对窗口进行其他特殊限制,详见[OpenMLDB特有的 WindowSpec 元素](#openmldb特有的-windowspec-元素)
- `WINDOW UNION` source 要求,支持如下格式的子查询:
- 表引用或者简单列筛选,例如 `t1` 或者 `select id, val from t1`。union source 和 主表的 schema 必须完全一致,并且 union source 对应的 `PARTITION BY`, `ORDER BY` 也需要命中索引
Expand Down
29 changes: 20 additions & 9 deletions docs/zh/openmldb_sql/dql/JOIN_CLAUSE.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
# JOIN Clause

OpenMLDB目前仅支持`LAST JOIN`一种**JoinType**
OpenMLDB目前支持

LAST JOIN可以看作一种特殊的LEFT JOIN。在满足JOIN条件的前提下,左表的每一行拼接符合条件的最后一行。LAST JOIN分为无排序拼接,和排序拼接。
- LAST JOIN
- LEFT JOIN (**OPENMLDB >= 0.8.4**)

LEFT OUTER JOIN (或者简称 LEFT JOIN) 会将两个 from_item 进行联接, 同时保留左侧from_item中的所有记录, 即使右侧from_item满足联接条件的记录数为零。对于右侧表中没有找到匹配的记录,则右侧的列会以 NULL 值填充。

LAST JOIN 是 OpenMLDB SQL 拓展的 JOIN类型. 它的语法和 LEFT JOIN 基本一致, 但在右侧 from_item 后面允许带可选的 ORDER BY 子句, 表示筛选右侧 from_iem 的顺序. 根据是否带有这个 ORDER BY 子句, LAST JOIN分为无排序拼接,和排序拼接。

- 无排序拼接是指:未对右表作排序,直接拼接。
- 排序拼接是指:先对右表排序,然后再拼接。

与LEFT JOIN相同,LAST JOIN也会返回左表中所有行,即使右表中没有匹配的行。
与LEFT JOIN相同,LAST JOIN也会返回左表中所有行,即使右表中没有匹配的行。不同的是, LAST JOIN 是一对一, LEFT JOIN 是一对多.

## Syntax

```
JoinClause
::= TableRef JoinType 'JOIN' TableRef [OrderByClause] 'ON' Expression
join:
TableRef "LAST" "JOIN" TableRef [OrderByClause] "ON" Expression
| TableRef join_type "JOIN" TableRef "ON" Expression
JoinType ::= 'LAST'
join_type:
'LEFT' [OUTER]
OrderByClause := 'ORDER' 'BY' <COLUMN_NAME>
order_by_clause:
'ORDER' 'BY' <COLUMN_NAME>
```

### 使用限制说明
Expand All @@ -30,14 +38,17 @@ OrderByClause := 'ORDER' 'BY' <COLUMN_NAME>
## SQL语句模版

```sql
SELECT ... FROM table_ref LAST JOIN table_ref ON expression;
SELECT ... FROM t1 LAST JOIN t2 ON expression;

SELECT ... FROM t1 LEFT JOIN t2 ON expression;
```

## 边界说明

| SELECT语句元素 | 离线模式 | 在线预览模式 | 在线请求模式 | 说明 |
| :--------------------------------------------- | --------- | ------------ | ------------ |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| JOIN Clause| **````** | **``x``** | **````** | 表示数据来源多个表JOIN。OpenMLDB目前仅支持LAST JOIN。在线请求模式下,需要遵循[在线请求模式下LAST JOIN的使用规范](../deployment_manage/ONLINE_REQUEST_REQUIREMENTS.md#在线请求模式下-last-join-的使用规范) |
| LAST JOIN | **````** | **``x``** | **````** | 表示数据来源多个表JOIN。在线请求模式下,需要遵循[在线请求模式下LAST JOIN的使用规范](../deployment_manage/ONLINE_REQUEST_REQUIREMENTS.md#在线请求模式下-last-join-的使用规范) |
| LEFT JOIN | **``x``** | **``x``** | **````** | 由于 LEFT JOIN 是一对多 JOIN, 本身不能直接用于在线请求模式. 但是可以作为其他类型查询内部的子查询, 例如作为 LAST JOIN 的右表. 具体参考[在线请求模式下LAST JOIN的使用规范](../deployment_manage/ONLINE_REQUEST_REQUIREMENTS.md#在线请求模式下-last-join-的使用规范) |


### 未排序的LAST JOIN
Expand Down
15 changes: 12 additions & 3 deletions docs/zh/openmldb_sql/dql/WINDOW_CLAUSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ SELECT select_expr [,select_expr...], window_function_name(expr) OVER window_nam

## 基本的 WindowSpec 语法元素

### Window Partition Clause 和 Window OrderBy Clause
### WINDOW PARTITION BY clause 和 WINDOW ORDER BY clause

```sql
WindowPartitionClause
Expand All @@ -145,9 +145,18 @@ WindowOrderByClause
::= ( 'ORDER' 'BY' ByList )
```

`PARTITION BY`选项将查询的行分为一组进入*partitions*, 这些行在窗口函数中单独处理。`PARTITION BY`和查询级别`GROUP BY` 子句做相似的工作,除了它的表达式只能作为表达式不能作为输出列的名字或数。OpenMLDB要求必须配置`PARTITION BY`。并且目前**仅支持按列分组**,无法支持按运算和函数表达式分组。
`PARTITION BY`选项将查询的行分为一组进入*partitions*, 这些行在窗口函数中单独处理。`PARTITION BY`和查询级别`GROUP BY` 子句做相似的工作, 只是它只能作为表达式不能作为查询结果的输出列或输出列 ID。OpenMLDB要求必须配置`PARTITION BY`。PARTITION BY list 可以有多个, 但**仅支持按列分组**,无法支持按运算或函数表达式分组。

`ORDER BY` 选项决定分区中的行被窗口函数处理的顺序。它和查询级别`ORDER BY`子句做相似的工作, 同样不能作为查询结果的输出列或者输出列 ID。OpenMLDB 目前**仅支持按列排序**,ORDER BY list 有且只能有一个, 不支持按运算或函数表达式排序。**OpenMLDB 0.8.4** 以后, 在线模式下 ORDER BY 子句可以不写 (离线模式暂时不支持), 表示窗口内的列将以不确定的顺序处理, 不带 ORDER BY 子句的窗口需要额外满足如下条件:

1. 不能有`EXCLUDE CURRENT_TIME`
2. 对于 ROWS 类型窗口没有更多限制, 对于 ROWS_RANGE 类型窗口:
1. 窗口 FRAME 的边界不能是 `offset [OPEN] PRECEDING/FOLLOWING` 的格式, 目前情况只能为 `UNBOUNDED PRECEDING AND CURRENT ROW`

```{note}
窗口不带 ORDER BY 的情况, 意味着对于在线预览模式, 计算结果是不确定的, 无法预测哪些行进去了窗口哪些行没有. 同时对于一些通用窗口函数, 例如 `lag, first_value`, 在所有模式下得到的计算结果都是不确定的,无法预测窗口内行的先后顺序.
```

`ORDER BY` 选项决定分区中的行被窗口函数处理的顺序。它和查询级别`ORDER BY`子句做相似的工作, 但是同样的它不能作为输出列的名字或数。同样,OpenMLDB要求必须配置`ORDER BY`。并且目前**仅支持按列排序**,无法支持按运算和函数表达式排序。

### Window Frame Clause

Expand Down
Loading

0 comments on commit 1c153e0

Please sign in to comment.