Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(sql): update SQL syntax for WINDOW and JOIN #3555

Merged
merged 6 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 下的窗口聚合子查询不带主表. 详细见下面的例子
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不带主表的例子是?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个例子不能体现?可否加一个不是主表的例子。也要和“没有主表”区别开,“不带主表”这个词有点歧义

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- **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 @@ -119,7 +119,7 @@ partition key相等的所有行,还不是窗口,经由order by列排序后

## 基本的 WindowSpec 语法元素

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

```sql
WindowPartitionClause
Expand All @@ -129,9 +129,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
Loading