

# SQL

SQL查询是用 `TableEnvironment` 的 `sqlQuery()` 方法指定的。该方法将 SQL 查询的结果作为 `Table` 返回。`Table` 可用于[后续 SQ L和表 API 查询](common.html#mixing-table-api-and-sql)、[转换为 DataSet 或 DataStream](common.html#integration-with-datastream-and-dataset-api)或[写入TableSink](common.html#emit-a-table)。SQL 和表 API 查询可以无缝地混合在一起，并进行整体优化并转换成一个程序。

为了访问 SQL 查询中的表，必须[在 TableEnvironment 中注册](common.html#register-tables-in-the-catalog)。可以从[TableSource](common.html#register-a-tablesource)、[table](common.html#register-a-table)、[DataStream，或DataSet](common.html#register-a-datastream-or-dataset-as-table)注册表。或者，用户也可以[在表环境中注册外部目录](common.html#register-an-external-catalog)来指定数据源的位置。

为了方便起见，`Table.toString()` 会自动在其 `TableEnvironment` 中以唯一名称注册表并返回该名称。因此，`Table` 对象可以直接内联到 SQL 查询中(通过字符串连接)，如下面的示例所示。

**注意：** Flink 的 SQL 支持尚未完成。包含不支持的 SQL 特性的查询会导致 `TableException`。下面几节列出了在批处理表和流表上支持的 SQL 特性。

## 指定一个查询

下面的示例显示如何在已注册和内联的表上指定 SQL 查询。



```
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tableEnv = TableEnvironment.getTableEnvironment(env);

// ingest a DataStream from an external source
DataStream<Tuple3<Long, String, Integer>> ds = env.addSource(...);

// SQL query with an inlined (unregistered) table
Table table = tableEnv.fromDataStream(ds, "user, product, amount");
Table result = tableEnv.sqlQuery(
  "SELECT SUM(amount) FROM " + table + " WHERE product LIKE '%Rubber%'");

// SQL query with a registered table
// register the DataStream as table "Orders"
tableEnv.registerDataStream("Orders", ds, "user, product, amount");
// run a SQL query on the Table and retrieve the result as a new Table
Table result2 = tableEnv.sqlQuery(
  "SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");

// SQL update with a registered table
// create and register a TableSink
TableSink csvSink = new CsvTableSink("/path/to/file", ...);
String[] fieldNames = {"product", "amount"};
TypeInformation[] fieldTypes = {Types.STRING, Types.INT};
tableEnv.registerTableSink("RubberOrders", fieldNames, fieldTypes, csvSink);
// run a SQL update query on the Table and emit the result to the TableSink
tableEnv.sqlUpdate(
  "INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'");
```





```
val env = StreamExecutionEnvironment.getExecutionEnvironment
val tableEnv = TableEnvironment.getTableEnvironment(env)

// read a DataStream from an external source val ds: DataStream[(Long, String, Integer)] = env.addSource(...)

// SQL query with an inlined (unregistered) table val table = ds.toTable(tableEnv, 'user, 'product, 'amount)
val result = tableEnv.sqlQuery(
  s"SELECT SUM(amount) FROM $table WHERE product LIKE '%Rubber%'")

// SQL query with a registered table
// register the DataStream under the name "Orders" tableEnv.registerDataStream("Orders", ds, 'user, 'product, 'amount)
// run a SQL query on the Table and retrieve the result as a new Table val result2 = tableEnv.sqlQuery(
  "SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")

// SQL update with a registered table
// create and register a TableSink TableSink csvSink = new CsvTableSink("/path/to/file", ...)
val fieldNames: Array[String] = Array("product", "amount")
val fieldTypes: Array[TypeInformation[_]] = Array(Types.STRING, Types.INT)
tableEnv.registerTableSink("RubberOrders", fieldNames, fieldTypes, csvSink)
// run a SQL update query on the Table and emit the result to the TableSink tableEnv.sqlUpdate(
  "INSERT INTO RubberOrders SELECT product, amount FROM Orders WHERE product LIKE '%Rubber%'")
```



## 支持语法

Flink 使用 [Apache Calcite](https://calcite.apache.org/docs/reference.html)解析SQL，它支持标准的 ANSI SQL。Flink 不支持 DDL 语句。

下面的 BNF 语法(BNF-grammar)描述了批处理和流查询中支持的 SQL 特性的超集。[Operations](#operations)小节展示了支持的特性的示例，并指出哪些特性只支持批处理或流查询。



```
insert:
  INSERT INTO tableReference
  query

query:
  values
  | {
      select
      | selectWithoutFrom
      | query UNION [ ALL ] query
      | query EXCEPT query
      | query INTERSECT query
    }
    [ ORDER BY orderItem [, orderItem ]* ]
    [ LIMIT { count | ALL } ]
    [ OFFSET start { ROW | ROWS } ]
    [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY]

orderItem:
  expression [ ASC | DESC ]

select:
  SELECT [ ALL | DISTINCT ]
  { * | projectItem [, projectItem ]* }
  FROM tableExpression
  [ WHERE booleanExpression ]
  [ GROUP BY { groupItem [, groupItem ]* } ]
  [ HAVING booleanExpression ]
  [ WINDOW windowName AS windowSpec [, windowName AS windowSpec ]* ]

selectWithoutFrom:
  SELECT [ ALL | DISTINCT ]
  { * | projectItem [, projectItem ]* }

projectItem:
  expression [ [ AS ] columnAlias ]
  | tableAlias . *

tableExpression:
  tableReference [, tableReference ]*
  | tableExpression [ NATURAL ] [ LEFT | RIGHT | FULL ] JOIN tableExpression [ joinCondition ]

joinCondition:
  ON booleanExpression
  | USING '(' column [, column ]* ')'

tableReference:
  tablePrimary
  [ matchRecognize ]
  [ [ AS ] alias [ '(' columnAlias [, columnAlias ]* ')' ] ]

tablePrimary:
  [ TABLE ] [ [ catalogName . ] schemaName . ] tableName
  | LATERAL TABLE '(' functionName '(' expression [, expression ]* ')' ')'
  | UNNEST '(' expression ')'

values:
  VALUES expression [, expression ]*

groupItem:
  expression
  | '(' ')'
  | '(' expression [, expression ]* ')'
  | CUBE '(' expression [, expression ]* ')'
  | ROLLUP '(' expression [, expression ]* ')'
  | GROUPING SETS '(' groupItem [, groupItem ]* ')'

windowRef:
    windowName
  | windowSpec

windowSpec:
    [ windowName ]
    '('
    [ ORDER BY orderItem [, orderItem ]* ]
    [ PARTITION BY expression [, expression ]* ]
    [
        RANGE numericOrIntervalExpression {PRECEDING}
      | ROWS numericExpression {PRECEDING}
    ]
    ')'

matchRecognize:
      MATCH_RECOGNIZE '('
      [ PARTITION BY expression [, expression ]* ]
      [ ORDER BY orderItem [, orderItem ]* ]
      [ MEASURES measureColumn [, measureColumn ]* ]
      [ ONE ROW PER MATCH ]
      [ AFTER MATCH
            ( SKIP TO NEXT ROW
            | SKIP PAST LAST ROW
            | SKIP TO FIRST variable
            | SKIP TO LAST variable
            | SKIP TO variable )
      ]
      PATTERN '(' pattern ')'
      DEFINE variable AS condition [, variable AS condition ]*
      ')'

measureColumn:
      expression AS alias

pattern:
      patternTerm [ '|' patternTerm ]*

patternTerm:
      patternFactor [ patternFactor ]*

patternFactor:
      variable [ patternQuantifier ]

patternQuantifier:
      '*'
  |   '*?'
  |   '+'
  |   '+?'
  |   '?'
  |   '??'
  |   '{' { [ minRepeat ], [ maxRepeat ] } '}' ['?']
  |   '{' repeat '}'
```



Flink SQL 对标识符(表、属性、函数名)使用词法策略，类似于Java:

*   无论是否引用标识符，都会保留标识符的大小写。
*   之后，标识符将区分大小写地匹配。
*   与 Java 不同，反号(back-ticks)允许标识符包含非字母数字字符(non-alphanumeric characters)(例如 `"SELECT a AS `my field` FROM t"`)。

字符串文字必须用单引号括起来(例如，`SELECT 'Hello World'`)。复制一个转义单引号(例如，`SELECT 'It''s me.'`)。Unicode 字符支持字符串文字。如果需要显式 unicode 代码点，请使用以下语法:

*   使用反斜杠(`\`)作为转义字符(默认): `SELECT U&'\263A'`
*   使用自定义转义字符: `SELECT U&'#263A' UESCAPE '#'`

## 操作 Operations

### 扫描、投影和过滤(Scan, Projection, and Filter)

| 操作 | 描述 |
| --- | --- |
| **Scan / Select / As**
Batch Streaming |



```
SELECT * FROM Orders

SELECT a, c AS d FROM Orders
```



 |
| **Where / Filter**
Batch Streaming |



```
SELECT * FROM Orders WHERE b = 'red'

SELECT * FROM Orders WHERE a % 2 = 0
```



 |
| **User-defined Scalar Functions (Scalar UDF)**
Batch Streaming | UDF 必须在表环境(TableEnvironment)中注册。有关如何指定和注册标量UDF的详细信息，请参阅[UDF文档](udfs.html)。



```
SELECT PRETTY_PRINT(user) FROM Orders
```



 |

### 聚合(Aggregations)

| 操作 | 描述 |
| --- | --- |
| **GroupBy Aggregation**
Batch Streaming
Result Updating | **注意:** GroupBy在流表上生成更新结果。有关详细信息，请参阅[Dynamic Tables Streaming Concepts](streaming/dynamic_tables.html)页面。



```
SELECT a, SUM(b) as d
FROM Orders
GROUP BY a
```



 |
| **GroupBy Window Aggregation**
Batch Streaming | Use a group window to compute a single result row per group. See [Group Windows](#group-windows) section for more details.
使用组窗口(group window)计算每个组的单个结果行。有关更多细节，请参见[Group Windows](#group-windows)一节。



```
SELECT user, SUM(amount)
FROM Orders
GROUP BY TUMBLE(rowtime, INTERVAL '1' DAY), user
```



 |
| **Over Window aggregation**
Streaming | **注意:** 所有聚合必须在同一个窗口上定义，即相同的分区、排序和范围。目前，只支持具有当前行范围之前(无界和有界)的窗口。还不支持包含以下内容的范围。ORDER BY必须在单个[time属性]上指定(streaming/time_attributes.html)



```
SELECT COUNT(amount) OVER (
  PARTITION BY user
  ORDER BY proctime
  ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
FROM Orders

SELECT COUNT(amount) OVER w, SUM(amount) OVER w
FROM Orders
WINDOW w AS (
  PARTITION BY user
  ORDER BY proctime
  ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
```



 |
| **Distinct**
Batch Streaming
Result Updating |



```
SELECT DISTINCT users FROM Orders
```



**注意:** 对于流查询，计算查询结果所需的状态可能会根据不同字段的数量无限增长。请提供具有有效保留间隔的查询配置，以防止状态过大。有关详细信息，请参见[查询配置](streaming/query_configuration.html)。 |
| **Grouping sets, Rollup, Cube**
Batch |



```
SELECT SUM(amount)
FROM Orders
GROUP BY GROUPING SETS ((user), (product))
```



 |
| **Having**
Batch Streaming |



```
SELECT SUM(amount)
FROM Orders
GROUP BY users
HAVING SUM(amount) &gt; 50
```



 |
| **User-defined Aggregate Functions (UDAGG)**
Batch Streaming | UDAGG 必须在表环境中注册。有关如何指定和注册 UDAGG 的详细信息，请参阅[UDF文档](udfs.html)。



```
SELECT MyAggregate(amount)
FROM Orders
GROUP BY users
```



 |

### 连接 Joins

| 操作 | 描述 |
| --- | --- |
| **Inner Equi-join**
Batch Streaming | 目前，只支持等值连接(equi-joins)，即，连接具有至少一个连接条件并带有相等谓词的连接。不支持任意交叉或连接。**注:** 连接顺序未优化。表按照 FROM 子句中指定的顺序连接。确保以不产生交叉连接（笛卡尔积）的顺序指定表，这些表不受支持并且会导致查询失败。



```
SELECT *
FROM Orders INNER JOIN Product ON Orders.productId = Product.id
```



**注意:** 对于流查询，计算查询结果所需的状态可能会根据不同的输入行数无限增长。请提供具有有效保留间隔的查询配置，以防止状态过大。有关详细信息，请参见[查询配置](streaming/query_configuration.html)。 |
| **Outer Equi-join**
Batch Streaming Result Updating | 目前，只支持等值连接(equi-joins)，即，具有至少一个连接条件并带有相等谓词的连接。不支持任意交叉或连接。**注意:** 连接顺序未优化。表按照 FROM 子句中指定的顺序连接。确保以不产生交叉连接（笛卡尔积）的顺序指定表，这些表不受支持并且会导致查询失败。



```
SELECT *
FROM Orders LEFT JOIN Product ON Orders.productId = Product.id

SELECT *
FROM Orders RIGHT JOIN Product ON Orders.productId = Product.id

SELECT *
FROM Orders FULL OUTER JOIN Product ON Orders.productId = Product.id
```



**注意:** 对于流查询，计算查询结果所需的状态可能会根据不同的输入行数无限增长。请提供具有有效保留间隔的查询配置，以防止状态过大。有关详细信息，请参见[查询配置](streaming/query_configuration.html)。 |
| **Time-windowed Join**
Batch Streaming | **注意:** 时间窗口连接是可以以流方式处理的常规连接的子集。有时间窗口的连接至少需要一个等值连接谓词和一个连接条件，该条件在连接两边限定时间。这样的条件可以由两个适当的范围谓词(`&lt;, &lt;=, &gt;=, &gt;`)、 `BETWEEN` 谓词或单个等式谓词来定义，该单个等式谓词比较两个输入表的相同类型（即处理时间或事件时间）的[时间属性](streaming/time_attributes.html)例如，以下谓词是有效的窗口连接条件:

*   `ltime = rtime`
*   `ltime &gt;= rtime AND ltime &lt; rtime + INTERVAL '10' MINUTE`
*   `ltime BETWEEN rtime - INTERVAL '10' SECOND AND rtime + INTERVAL '5' SECOND`



```
SELECT *
FROM Orders o, Shipments s
WHERE o.id = s.orderId AND
      o.ordertime BETWEEN s.shiptime - INTERVAL '4' HOUR AND s.shiptime
```



如果订单是在收到订单后4小时发出的，那么上面的示例将把所有订单与其相应的发货连接起来。 |
| **Expanding arrays into a relation**
Batch Streaming | 不支持使用序数反嵌套(Unnesting WITH ORDINALITY)。



```
SELECT users, tag
FROM Orders CROSS JOIN UNNEST(tags) AS t (tag)
```



 |
| **Join with Table Function**
Batch Streaming | 将表与表函数的结果连接在一起。左边(外部)表的每一行都与表函数的相应调用生成的所有行连接在一起。必须在此之前注册用户定义的表函数(UDTFs)。有关如何指定和注册UDTFs的详细信息，请参阅[UDF文档](udfs.html)。**内连接(Inner Join)** 如果表函数调用返回空结果，则删除左侧(外部)表的一行。



```
SELECT users, tag
FROM Orders, LATERAL TABLE(unnest_udtf(tags)) t AS tag
```



**左外连接(Left Outer Join)** 如果表函数调用返回空结果，则保留相应的外部行，并使用空值填充结果。



```
SELECT users, tag
FROM Orders LEFT JOIN LATERAL TABLE(unnest_udtf(tags)) t AS tag ON TRUE
```



**注意：** 目前，只支持字面上的 `TRUE` 作为针对横向表的左外连接的谓词。 |
| **Join with Temporal Table**
Streaming | [时态表](streaming/temporal_tables.html)是跟踪随时间变化的表。[时态表函数](streaming/temporal_tables.html#temporal-table-functions)提供对特定时间点时态表状态的访问。使用时态表函数连接表的语法与 _表函数联接(Join with Table Function)_ 中的语法相同。**注意:** 目前只支持带时态表的内部连接。假设 _Rates_ 是[时态表函数](streaming/temporal_tables.html#temporal-table-functions)，连接可以用 SQL 表示如下:



```
SELECT
  o_amount, r_rate
FROM
  Orders,
  LATERAL TABLE (Rates(o_proctime))
WHERE
  r_currency = o_currency
```



有关更多信息，请查看更详细的[时态表概念描述](streaming/temporal_tables.html)。 |

### 集合操作(Set Operations)

| 操作 | 描述 |
| --- | --- |
| **Union**
Batch |



```
SELECT *
FROM (
    (SELECT user FROM Orders WHERE a % 2 = 0)
  UNION
    (SELECT user FROM Orders WHERE b = 0)
)
```



 |
| **UnionAll**
Batch Streaming |



```
SELECT *
FROM (
    (SELECT user FROM Orders WHERE a % 2 = 0)
  UNION ALL
    (SELECT user FROM Orders WHERE b = 0)
)
```



 |
| **Intersect / Except**
Batch |



```
SELECT *
FROM (
    (SELECT user FROM Orders WHERE a % 2 = 0)
  INTERSECT
    (SELECT user FROM Orders WHERE b = 0)
)
```





```
SELECT *
FROM (
    (SELECT user FROM Orders WHERE a % 2 = 0)
  EXCEPT
    (SELECT user FROM Orders WHERE b = 0)
)
```



 |
| **In**
Batch Streaming | 如果给定表子查询中存在表达式，则返回 true。子查询表必须由一列组成。该列必须具有与表达式相同的数据类型。



```
SELECT user, amount
FROM Orders
WHERE product IN (
    SELECT product FROM NewProducts
)
```



**注意:** 对于流查询，操作在 join 和 group 操作中重写。计算查询结果所需的状态可能会无限增长，这取决于不同输入行的数量。请提供具有有效保留间隔的查询配置，以防止状态过大。有关详细信息，请参见[查询配置](streaming/query_configuration.html)。 |
| **Exists**
Batch Streaming | 如果子查询至少返回一行，则返回true。只有在可以在 join 和 group 操作中重写操作时，才支持该操作。



```
SELECT user, amount
FROM Orders
WHERE product EXISTS (
    SELECT product FROM NewProducts
)
```



**注意** 对于流查询，操作在 join 和 group 操作中重写。计算查询结果所需的状态可能会无限增长，这取决于不同输入行的数量。请提供具有有效保留间隔的查询配置，以防止状态过大。有关详细信息，请参见[查询配置](streaming/query_configuration.html)。 |

### OrderBy & Limit

| 操作 | 描述 |
| --- | --- |
| **Order By**
Batch Streaming | **注意：** 流查询的结果必须主要根据升序[time 属性](streaming/time_attributes.html)进行排序。还支持其他排序属性。



```
SELECT *
FROM Orders
ORDER BY orderTime
```



 |
| **Limit**
Batch |



```
SELECT *
FROM Orders
LIMIT 3
```



 |

### 插入(Insert)

| 操作 | 描述 |
| --- | --- |
| **Insert Into**
Batch Streaming | 输出表必须在 TableEnvironment 中注册(参见[Register a TableSink](common.html#register-a-tablesink))。此外，已注册表的模式必须与查询的模式匹配。



```
INSERT INTO OutputTable
SELECT users, tag
FROM Orders
```



 |

### Group Windows

Group 窗口(Group windows)是在SQL查询的 `GROUP BY` 子句中定义的。就像使用常规的 `GROUP BY` 子句查询一样，使用包含 GROUP 窗口函数的 `GROUP BY` 子句查询会为每个组计算一个结果行。批处理表和流表上的 SQL 支持以下 group windows 函数。

| Group 窗口函数 | 描述 |
| --- | --- |
| `TUMBLE(time_attr, interval)` | 定义一个滚动时间窗口。滚动时间窗口将行分配给具有固定持续时间(`间隔(interval)`)的非重叠连续窗口。例如，一个5分钟滚动窗口以5分钟为间隔将行分组。滚动窗口可以在事件时间(流+批处理)或处理时间(流)上定义。 |
| `HOP(time_attr, interval, interval)` | 定义一个跳转时间窗口(hopping time window)(在表 API 中称为滑动窗口)。跳转时间窗口具有固定的持续时间(第二个 `interval` 参数)，并按指定的跳转间隔(第一个 `interval` 参数)跳转。如果跳转间隔小于窗口大小，则跳转窗口重叠。因此，可以将行分配给多个窗口。例如，一个15分钟大小的跳转窗口和5分钟的跳转间隔将每一行分配给3个15分钟大小的不同窗口，这些窗口在5分钟的间隔内计算。跳跃窗口可以在事件时间(流+批处理)或处理时间(流)上定义。 |
| `SESSION(time_attr, interval)` | 定义会话时间窗口(session time window)。会话时间窗口没有固定的持续时间，但它们的边界由不活动的时间 `间隔(interval)` 定义，即，如果在定义的间隔期间没有出现事件，则关闭会话窗口。例如，如果在30分钟不活动之后观察到一行(否则该行将被添加到现有窗口)，则会启动一个间隔为30分钟的会话窗口，如果在30分钟内没有添加行，则会关闭该会话窗口。会话窗口可以在事件时间(流+批处理)或处理时间(流)上工作。 |

#### 时间属性

对于流表上的 SQL 查询，组窗口函数的 `time_attr` 参数必须引用一个有效的时间属性，该属性指定行处理时间或事件时间。参见[时间属性文档](streaming/time_attributes.html)了解如何定义时间属性。

对于批处理表上的 SQL，组窗口函数的 `time_attr` 参数必须是 `TIMESTAMP` 类型的属性。

#### 选择 Group 窗口开始和结束时间戳

Group 窗口的开始和结束时间戳以及时间属性可以通过以下辅助函数(auxiliary functions)来选择:

| 辅助函数 | 描述 |
| --- | --- |
| `TUMBLE_START(time_attr, interval)`
`HOP_START(time_attr, interval, interval)`
`SESSION_START(time_attr, interval)`
 | 返回相应滚动、跳跃或会话窗口的包含下界的时间戳。 |
| `TUMBLE_END(time_attr, interval)`
`HOP_END(time_attr, interval, interval)`
`SESSION_END(time_attr, interval)`
 | 返回相应滚动、跳跃或会话窗口的 _独占(exclusive)_ 上界的时间戳。**注意:** 在后续基于时间的操作中，例如[时间窗口连接](#joins)和[组窗口或窗口之上的聚合](#aggregations))中，_不能_ 将独占的上界时间戳用作[rowtime 属性](streaming/time_attributes.html)。 |
| `TUMBLE_ROWTIME(time_attr, interval)`
`HOP_ROWTIME(time_attr, interval, interval)`
`SESSION_ROWTIME(time_attr, interval)`
 | 返回相应滚动、跳跃或会话窗口的 _独占(inclusive)_ 上界的时间戳。得到的属性是一个[rowtime 属性](streaming/time_attributes.html)，可以在后续基于时间的操作中使用，比如[时间窗口连接](#joins)和[组窗口或窗口之上的聚合](#aggregations)。 |
| `TUMBLE_PROCTIME(time_attr, interval)`
`HOP_PROCTIME(time_attr, interval, interval)`
`SESSION_PROCTIME(time_attr, interval)`
 | 返回一个[proctime 属性](streaming/time_attributes.html#processing-time)，该属性可用于后续基于时间的操作，如[时间窗口连接](#joins)和[组窗口或窗口聚合之上的窗口](#aggregations)。 |

_注意：_ 必须使用与 `GROUP BY` 子句中的组窗口函数完全相同的参数调用辅助函数。

下面的示例显示如何在流表上使用组窗口指定 SQL 查询。



```
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tableEnv = TableEnvironment.getTableEnvironment(env);

// ingest a DataStream from an external source
DataStream<Tuple3<Long, String, Integer>> ds = env.addSource(...);
// register the DataStream as table "Orders"
tableEnv.registerDataStream("Orders", ds, "user, product, amount, proctime.proctime, rowtime.rowtime");

// compute SUM(amount) per day (in event-time)
Table result1 = tableEnv.sqlQuery(
  "SELECT user, " +
  "  TUMBLE_START(rowtime, INTERVAL '1' DAY) as wStart,  " +
  "  SUM(amount) FROM Orders " +
  "GROUP BY TUMBLE(rowtime, INTERVAL '1' DAY), user");

// compute SUM(amount) per day (in processing-time)
Table result2 = tableEnv.sqlQuery(
  "SELECT user, SUM(amount) FROM Orders GROUP BY TUMBLE(proctime, INTERVAL '1' DAY), user");

// compute every hour the SUM(amount) of the last 24 hours in event-time
Table result3 = tableEnv.sqlQuery(
  "SELECT product, SUM(amount) FROM Orders GROUP BY HOP(rowtime, INTERVAL '1' HOUR, INTERVAL '1' DAY), product");

// compute SUM(amount) per session with 12 hour inactivity gap (in event-time)
Table result4 = tableEnv.sqlQuery(
  "SELECT user, " +
  "  SESSION_START(rowtime, INTERVAL '12' HOUR) AS sStart, " +
  "  SESSION_ROWTIME(rowtime, INTERVAL '12' HOUR) AS snd, " +
  "  SUM(amount) " +
  "FROM Orders " +
  "GROUP BY SESSION(rowtime, INTERVAL '12' HOUR), user");
```





```
val env = StreamExecutionEnvironment.getExecutionEnvironment
val tableEnv = TableEnvironment.getTableEnvironment(env)

// read a DataStream from an external source val ds: DataStream[(Long, String, Int)] = env.addSource(...)
// register the DataStream under the name "Orders" tableEnv.registerDataStream("Orders", ds, 'user, 'product, 'amount, 'proctime.proctime, 'rowtime.rowtime)

// compute SUM(amount) per day (in event-time) val result1 = tableEnv.sqlQuery(
    """
      |SELECT
      |  user,
      |  TUMBLE_START(rowtime, INTERVAL '1' DAY) as wStart,
      |  SUM(amount)
      | FROM Orders
      | GROUP BY TUMBLE(rowtime, INTERVAL '1' DAY), user
    """.stripMargin)

// compute SUM(amount) per day (in processing-time) val result2 = tableEnv.sqlQuery(
  "SELECT user, SUM(amount) FROM Orders GROUP BY TUMBLE(proctime, INTERVAL '1' DAY), user")

// compute every hour the SUM(amount) of the last 24 hours in event-time val result3 = tableEnv.sqlQuery(
  "SELECT product, SUM(amount) FROM Orders GROUP BY HOP(rowtime, INTERVAL '1' HOUR, INTERVAL '1' DAY), product")

// compute SUM(amount) per session with 12 hour inactivity gap (in event-time) val result4 = tableEnv.sqlQuery(
    """
      |SELECT
      |  user,
      |  SESSION_START(rowtime, INTERVAL '12' HOUR) AS sStart,
      |  SESSION_END(rowtime, INTERVAL '12' HOUR) AS sEnd,
      |  SUM(amount)
      | FROM Orders
      | GROUP BY SESSION(rowtime(), INTERVAL '12' HOUR), user
    """.stripMargin)
```



### 模式识别

| 操作 | 描述 |
| --- | --- |
| **MATCH_RECOGNIZE**
Streaming | 根据 `MATCH_RECOGNIZE` [ISO标准](https://standards.iso.org/ittf/PubliclyAvailableStandards/c065143_ISO_IEC_TR_19075-5_2016.zip)在流表中搜索给定的模式。这使得在 SQL 查询中表达复杂事件处理(CEP)逻辑成为可能。有关更详细的描述，请参见[检测表中的模式](streaming/match_recognize.html)的专用页面。



```
SELECT T.aid, T.bid, T.cid
FROM MyTable
MATCH_RECOGNIZE (
  PARTITION BY userid
  ORDER BY proctime
  MEASURES
    A.id AS aid,
    B.id AS bid,
    C.id AS cid
  PATTERN (A B C)
  DEFINE
    A AS name = 'a',
    B AS name = 'b',
    C AS name = 'c'
) AS T
```



 |

## 数据类型

The SQL runtime is built on top of Flink’s DataSet and DataStream APIs. Internally, it also uses Flink’s `TypeInformation` to define data types. Fully supported types are listed in `org.apache.flink.table.api.Types`. The following table summarizes the relation between SQL Types, Table API types, and the resulting Java class.
SQL 运行时构建在 Flink 的数据集(DataSet)和数据流(DataStream) API 之上。在内部，它还使用 Flink 的 `TypeInformation` 来定义数据类型。完全支持的类型列在 `org.apache.flink.table.api.Types` 中。下表总结了 SQL 类型、表 API 类型和生成的 Java 类之间的关系。

| Table API | SQL | Java type |
| --- | --- | --- |
| `Types.STRING` | `VARCHAR` | `java.lang.String` |
| `Types.BOOLEAN` | `BOOLEAN` | `java.lang.Boolean` |
| `Types.BYTE` | `TINYINT` | `java.lang.Byte` |
| `Types.SHORT` | `SMALLINT` | `java.lang.Short` |
| `Types.INT` | `INTEGER, INT` | `java.lang.Integer` |
| `Types.LONG` | `BIGINT` | `java.lang.Long` |
| `Types.FLOAT` | `REAL, FLOAT` | `java.lang.Float` |
| `Types.DOUBLE` | `DOUBLE` | `java.lang.Double` |
| `Types.DECIMAL` | `DECIMAL` | `java.math.BigDecimal` |
| `Types.SQL_DATE` | `DATE` | `java.sql.Date` |
| `Types.SQL_TIME` | `TIME` | `java.sql.Time` |
| `Types.SQL_TIMESTAMP` | `TIMESTAMP(3)` | `java.sql.Timestamp` |
| `Types.INTERVAL_MONTHS` | `INTERVAL YEAR TO MONTH` | `java.lang.Integer` |
| `Types.INTERVAL_MILLIS` | `INTERVAL DAY TO SECOND(3)` | `java.lang.Long` |
| `Types.PRIMITIVE_ARRAY` | `ARRAY` | e.g. `int[]` |
| `Types.OBJECT_ARRAY` | `ARRAY` | e.g. `java.lang.Byte[]` |
| `Types.MAP` | `MAP` | `java.util.HashMap` |
| `Types.MULTISET` | `MULTISET` | e.g. `java.util.HashMap&lt;String, Integer&gt;` for a multiset of `String` |
| `Types.ROW` | `ROW` | `org.apache.flink.types.Row` |

泛型类型和(嵌套的)复合类型(例如POJOs, tuples, rows, Scala case classes)也可以是一行的字段。

可以使用[value access functions](functions.html#value-access-functions)访问具有任意嵌套的复合类型字段。

泛型类型被视为一个黑盒子，可以通过[用户定义函数](udfs.html)传递或处理。

## 保留关键字

虽然还没有实现所有 SQL 特性，但是一些字符串组合已经被保留为关键字，以供将来使用。如果您想使用下列字符串之一作为字段名，请确保用反引号(例如 ``value``, ``count``)。



```
A, ABS, ABSOLUTE, ACTION, ADA, ADD, ADMIN, AFTER, ALL, ALLOCATE, ALLOW, ALTER, ALWAYS, AND, ANY, ARE, ARRAY, AS, ASC, ASENSITIVE, ASSERTION, ASSIGNMENT, ASYMMETRIC, AT, ATOMIC, ATTRIBUTE, ATTRIBUTES, AUTHORIZATION, AVG, BEFORE, BEGIN, BERNOULLI, BETWEEN, BIGINT, BINARY, BIT, BLOB, BOOLEAN, BOTH, BREADTH, BY, C, CALL, CALLED, CARDINALITY, CASCADE, CASCADED, CASE, CAST, CATALOG, CATALOG_NAME, CEIL, CEILING, CENTURY, CHAIN, CHAR, CHARACTER, CHARACTERISTICS, CHARACTERS, CHARACTER_LENGTH, CHARACTER_SET_CATALOG, CHARACTER_SET_NAME, CHARACTER_SET_SCHEMA, CHAR_LENGTH, CHECK, CLASS_ORIGIN, CLOB, CLOSE, COALESCE, COBOL, COLLATE, COLLATION, COLLATION_CATALOG, COLLATION_NAME, COLLATION_SCHEMA, COLLECT, COLUMN, COLUMN_NAME, COMMAND_FUNCTION, COMMAND_FUNCTION_CODE, COMMIT, COMMITTED, CONDITION, CONDITION_NUMBER, CONNECT, CONNECTION, CONNECTION_NAME, CONSTRAINT, CONSTRAINTS, CONSTRAINT_CATALOG, CONSTRAINT_NAME, CONSTRAINT_SCHEMA, CONSTRUCTOR, CONTAINS, CONTINUE, CONVERT, CORR, CORRESPONDING, COUNT, COVAR_POP, COVAR_SAMP, CREATE, CROSS, CUBE, CUME_DIST, CURRENT, CURRENT_CATALOG, CURRENT_DATE, CURRENT_DEFAULT_TRANSFORM_GROUP, CURRENT_PATH, CURRENT_ROLE, CURRENT_SCHEMA, CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_TRANSFORM_GROUP_FOR_TYPE, CURRENT_USER, CURSOR, CURSOR_NAME, CYCLE, DATA, DATABASE, DATE, DATETIME_INTERVAL_CODE, DATETIME_INTERVAL_PRECISION, DAY, DEALLOCATE, DEC, DECADE, DECIMAL, DECLARE, DEFAULT, DEFAULTS, DEFERRABLE, DEFERRED, DEFINED, DEFINER, DEGREE, DELETE, DENSE_RANK, DEPTH, DEREF, DERIVED, DESC, DESCRIBE, DESCRIPTION, DESCRIPTOR, DETERMINISTIC, DIAGNOSTICS, DISALLOW, DISCONNECT, DISPATCH, DISTINCT, DOMAIN, DOUBLE, DOW, DOY, DROP, DYNAMIC, DYNAMIC_FUNCTION, DYNAMIC_FUNCTION_CODE, EACH, ELEMENT, ELSE, END, END-EXEC, EPOCH, EQUALS, ESCAPE, EVERY, EXCEPT, EXCEPTION, EXCLUDE, EXCLUDING, EXEC, EXECUTE, EXISTS, EXP, EXPLAIN, EXTEND, EXTERNAL, EXTRACT, FALSE, FETCH, FILTER, FINAL, FIRST, FIRST_VALUE, FLOAT, FLOOR, FOLLOWING, FOR, FOREIGN, FORTRAN, FOUND, FRAC_SECOND, FREE, FROM, FULL, FUNCTION, FUSION, G, GENERAL, GENERATED, GET, GLOBAL, GO, GOTO, GRANT, GRANTED, GROUP, GROUPING, HAVING, HIERARCHY, HOLD, HOUR, IDENTITY, IMMEDIATE, IMPLEMENTATION, IMPORT, IN, INCLUDING, INCREMENT, INDICATOR, INITIALLY, INNER, INOUT, INPUT, INSENSITIVE, INSERT, INSTANCE, INSTANTIABLE, INT, INTEGER, INTERSECT, INTERSECTION, INTERVAL, INTO, INVOKER, IS, ISOLATION, JAVA, JOIN, K, KEY, KEY_MEMBER, KEY_TYPE, LABEL, LANGUAGE, LARGE, LAST, LAST_VALUE, LATERAL, LEADING, LEFT, LENGTH, LEVEL, LIBRARY, LIKE, LIMIT, LN, LOCAL, LOCALTIME, LOCALTIMESTAMP, LOCATOR, LOWER, M, MAP, MATCH, MATCHED, MAX, MAXVALUE, MEMBER, MERGE, MESSAGE_LENGTH, MESSAGE_OCTET_LENGTH, MESSAGE_TEXT, METHOD, MICROSECOND, MILLENNIUM, MIN, MINUTE, MINVALUE, MOD, MODIFIES, MODULE, MONTH, MORE, MULTISET, MUMPS, NAME, NAMES, NATIONAL, NATURAL, NCHAR, NCLOB, NESTING, NEW, NEXT, NO, NONE, NORMALIZE, NORMALIZED, NOT, NULL, NULLABLE, NULLIF, NULLS, NUMBER, NUMERIC, OBJECT, OCTETS, OCTET_LENGTH, OF, OFFSET, OLD, ON, ONLY, OPEN, OPTION, OPTIONS, OR, ORDER, ORDERING, ORDINALITY, OTHERS, OUT, OUTER, OUTPUT, OVER, OVERLAPS, OVERLAY, OVERRIDING, PAD, PARAMETER, PARAMETER_MODE, PARAMETER_NAME, PARAMETER_ORDINAL_POSITION, PARAMETER_SPECIFIC_CATALOG, PARAMETER_SPECIFIC_NAME, PARAMETER_SPECIFIC_SCHEMA, PARTIAL, PARTITION, PASCAL, PASSTHROUGH, PATH, PERCENTILE_CONT, PERCENTILE_DISC, PERCENT_RANK, PLACING, PLAN, PLI, POSITION, POWER, PRECEDING, PRECISION, PREPARE, PRESERVE, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE, PUBLIC, QUARTER, RANGE, RANK, READ, READS, REAL, RECURSIVE, REF, REFERENCES, REFERENCING, REGR_AVGX, REGR_AVGY, REGR_COUNT, REGR_INTERCEPT, REGR_R2, REGR_SLOPE, REGR_SXX, REGR_SXY, REGR_SYY, RELATIVE, RELEASE, REPEATABLE, RESET, RESTART, RESTRICT, RESULT, RETURN, RETURNED_CARDINALITY, RETURNED_LENGTH, RETURNED_OCTET_LENGTH, RETURNED_SQLSTATE, RETURNS, REVOKE, RIGHT, ROLE, ROLLBACK, ROLLUP, ROUTINE, ROUTINE_CATALOG, ROUTINE_NAME, ROUTINE_SCHEMA, ROW, ROWS, ROW_COUNT, ROW_NUMBER, SAVEPOINT, SCALE, SCHEMA, SCHEMA_NAME, SCOPE, SCOPE_CATALOGS, SCOPE_NAME, SCOPE_SCHEMA, SCROLL, SEARCH, SECOND, SECTION, SECURITY, SELECT, SELF, SENSITIVE, SEQUENCE, SERIALIZABLE, SERVER, SERVER_NAME, SESSION, SESSION_USER, SET, SETS, SIMILAR, SIMPLE, SIZE, SMALLINT, SOME, SOURCE, SPACE, SPECIFIC, SPECIFICTYPE, SPECIFIC_NAME, SQL, SQLEXCEPTION, SQLSTATE, SQLWARNING, SQL_TSI_DAY, SQL_TSI_FRAC_SECOND, SQL_TSI_HOUR, SQL_TSI_MICROSECOND, SQL_TSI_MINUTE, SQL_TSI_MONTH, SQL_TSI_QUARTER, SQL_TSI_SECOND, SQL_TSI_WEEK, SQL_TSI_YEAR, SQRT, START, STATE, STATEMENT, STATIC, STDDEV_POP, STDDEV_SAMP, STREAM, STRUCTURE, STYLE, SUBCLASS_ORIGIN, SUBMULTISET, SUBSTITUTE, SUBSTRING, SUM, SYMMETRIC, SYSTEM, SYSTEM_USER, TABLE, TABLESAMPLE, TABLE_NAME, TEMPORARY, THEN, TIES, TIME, TIMESTAMP, TIMESTAMPADD, TIMESTAMPDIFF, TIMEZONE_HOUR, TIMEZONE_MINUTE, TINYINT, TO, TOP_LEVEL_COUNT, TRAILING, TRANSACTION, TRANSACTIONS_ACTIVE, TRANSACTIONS_COMMITTED, TRANSACTIONS_ROLLED_BACK, TRANSFORM, TRANSFORMS, TRANSLATE, TRANSLATION, TREAT, TRIGGER, TRIGGER_CATALOG, TRIGGER_NAME, TRIGGER_SCHEMA, TRIM, TRUE, TYPE, UESCAPE, UNBOUNDED, UNCOMMITTED, UNDER, UNION, UNIQUE, UNKNOWN, UNNAMED, UNNEST, UPDATE, UPPER, UPSERT, USAGE, USER, USER_DEFINED_TYPE_CATALOG, USER_DEFINED_TYPE_CODE, USER_DEFINED_TYPE_NAME, USER_DEFINED_TYPE_SCHEMA, USING, VALUE, VALUES, VARBINARY, VARCHAR, VARYING, VAR_POP, VAR_SAMP, VERSION, VIEW, WEEK, WHEN, WHENEVER, WHERE, WIDTH_BUCKET, WINDOW, WITH, WITHIN, WITHOUT, WORK, WRAPPER, WRITE, XML, YEAR, ZONE
```
