Table API是流處理和批處理通用的關(guān)系型API,Table API可以基于流輸入或者批輸入來運(yùn)行而不需要進(jìn)行任何修改。Table API是SQL語言的超集并專門為Apache Flink設(shè)計(jì)的,Table API是Scala 和Java語言集成式的API。與常規(guī)SQL語言中將查詢指定為字符串不同,Table API查詢是以Java或Scala中的語言嵌入樣式來定義的,具有IDE支持如:自動完成和語法檢測。
Table API與Flink的SQL集成共享許多其API的概念和部分,請參考通用的概念和API來了解如何注冊table或者創(chuàng)建一個Table對象,Streaming Concepts頁討論了特殊的概念如:動態(tài)表和時間屬性。
接下來的例子中假設(shè)注冊了一個名叫Orders的表并有(a, b, c, rowtime)屬性,rowtime字段可以是流中的邏輯時間字段或者是批中的常規(guī)時間戳字段。
概述和實(shí)例
Table API可以用于Scala和Java中,Scala Table API利用了Scala表達(dá)式,Java Table API則是基于字符串來的,字符串會被解析并轉(zhuǎn)換成等價的表達(dá)式。
接下來的例子展示了Scala 和 Java Table API的不同之處,表程序是在批環(huán)境中執(zhí)行的,它掃描Orders表,根據(jù)a字段來分組,并計(jì)算每個分組的結(jié)果,表程序的結(jié)果轉(zhuǎn)換為一個Row類型的DataSet并打印出來。
Java Table API可以通過導(dǎo)入org.apache.flink.table.api.java.*
來啟用,下面的例子展示了Java Table API程序如何構(gòu)建及表達(dá)式如何指定為字符串。
// 配置環(huán)境
ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
BatchTableEnvironment tEnv = TableEnvironment.getTableEnvironment(env);
// 在表環(huán)境中注冊O(shè)rders表
// ...
// 指定一個表程序
Table orders = tEnv.scan("Orders"); // schema (a, b, c, rowtime)
Table counts = orders
.groupBy("a")
.select("a, b.count as cnt");
// 結(jié)果轉(zhuǎn)換為 DataSet
DataSet<Row> result = tableEnv.toDataSet(counts, Row.class);
result.print();
Scala Table API可以通過導(dǎo)入org.apache.flink.api.scala._
和org.apache.flink.table.api.scala._
包來啟用。
下面例子展示了Scala Table API如何構(gòu)建, Table屬性使用Scala表達(dá)式來引用,Scala表達(dá)式以`開頭:
import org.apache.flink.api.scala._
import org.apache.flink.table.api.scala._
// 配置環(huán)境
val env = ExecutionEnvironment.getExecutionEnvironment
val tEnv = TableEnvironment.getTableEnvironment(env)
//在表環(huán)境中注冊一個Orders表
// ...
// specify table program
val orders = tEnv.scan("Orders") // schema (a, b, c, rowtime)
val result = orders
.groupBy('a)
.select('a, 'b.count as 'cnt)
.toDataSet[Row] // 轉(zhuǎn)換為 DataSet
.print()
下面的例子展示了一個更加復(fù)雜的Table API程序,程序再次掃描Orders表,過濾空值,將a字符字段轉(zhuǎn)為小寫,并每小時計(jì)算一次產(chǎn)生一個平均費(fèi)用b:
// 配置環(huán)境
// ...
// 指定表程序
Table orders = tEnv.scan("Orders"); // schema (a, b, c, rowtime)
Table result = orders
.filter("a.isNotNull && b.isNotNull && c.isNotNull")
.select("a.lowerCase(), b, rowtime")
.window(Tumble.over("1.hour").on("rowtime").as("hourlyWindow"))
.groupBy("hourlyWindow, a")
.select("a, hourlyWindow.end as hour, b.avg as avgBillingAmount");
// 配置環(huán)境
// ...
// 指定表程序
val orders: Table = tEnv.scan("Orders") // schema (a, b, c, rowtime)
val result: Table = orders
.filter('a.isNotNull && 'b.isNotNull && 'c.isNotNull)
.select('a.lowerCase(), 'b, 'rowtime)
.window(Tumble over 1.hour on 'rowtime as 'hourlyWindow)
.groupBy('hourlyWindow, 'a)
.select('a, 'hourlyWindow.end as 'hour, 'b.avg as 'avgBillingAmount);
因?yàn)門able API是流數(shù)據(jù)和批數(shù)據(jù)統(tǒng)一的API,所有的示例程序都可以在批輸入或者流輸入中執(zhí)行而不需要修改程序。在兩種情況下都會產(chǎn)生相同的結(jié)果,使得流記錄不會延遲。
操作
Table API支持下面的操作,請注意并不是所有的操作都同時支持批程序和流程序,不支持的會被響應(yīng)的標(biāo)記出來。
Scan,Projection和Filter (掃描、映射和過濾)
操作 | 描述 |
---|---|
Scan | 與SQL中的FROM相似,對已注冊的表執(zhí)行掃描操作Table orders = tableEnv.scan("Orders");
|
Select | 與SQL的SELECT語義類似,執(zhí)行一個select操作Table orders = tableEnv.scan("Orders"); Table result = orders.select("a, c as d"); 你也可以使用(*)作為通配符,來查詢表中所有字段的值 Table result = orders.select("*");
|
As | 重命名字段Table orders = tableEnv.scan("Orders"); Table result = orders.as("x, y, z, t");
|
Where / Filter | 與SQL中的WHERE類似,過濾不通過過濾謂詞的行Table orders = tableEnv.scan("Orders"); Table result = orders.where("b === 'red'"); 或者 Table orders = tableEnv.scan("Orders"); Table result = orders.filter("a % 2 === 0");
|
操作 | 描述 |
---|---|
Scan | 與SQL查詢的From類似,對注冊的表執(zhí)行掃描操作val orders: Table = tableEnv.scan("Orders")
|
Select | 與SQL腳本的SELECT類似,執(zhí)行一個select操作:val orders: Table = tableEnv.scan("Orders") val result = orders.select('a, 'c as 'd) 你可以使用(*)作為通配符,查詢表中的所有字段 val orders: Table = tableEnv.scan("Orders") val result = orders.select('*)
|
As | 重命名字段:val orders: Table = tableEnv.scan("Orders").as('x, 'y, 'z, 't)
|
Where/Filter | 與SQL的WHERE類似,過濾掉不滿足過濾謂詞的行val orders: Table = tableEnv.scan("Orders") val result = orders.filter('a % 2 === 0) 或者val orders: Table = tableEnv.scan("Orders") val result = orders.where('b === "red")
|
聚合
操作 | 描述 |
---|---|
GroupBy 聚合 | 與SQL的GROUP BY類似,使用以下運(yùn)行的聚合運(yùn)算符對分組鍵上的行進(jìn)行分組,從而以組方式聚合行。Table orders = tableEnv.scan("Orders"); Table result = orders.groupBy("a").select("a, b.sum as d");
|
GroupBy Window 聚合 | 對分組窗口上的表,進(jìn)行分組和聚合,可能會按一個或者多個key進(jìn)行分組Table orders = tableEnv.scan("Orders"); Table orders = tableEnv.scan("Orders"); Table result = orders .window(Tumble.over("5.minutes").on("rowtime").as("w")) // define window .groupBy("a, w") // group by key and window .select("a, w.start, w.end, b.sum as d"); // access window properties and aggregate
|
Over Window 聚合 | 與SQL的OVER類似,根據(jù)前一行和后續(xù)行的窗口(范圍)為每行計(jì)算窗口聚合,參看over window獲取更多信息。Table orders = tableEnv.scan("Orders"); Table result = orders .window(Over .partitionBy("a") .orderBy("rowtime") .preceding("UNBOUNDED_RANGE") .following("CURRENT_RANGE") .as("w") .select("a, b.avg over w, b.max over w, b.min over w") // sliding aggregate
|
Distinct | 與SQL的DISTINCT類似,返回不重合的記錄。Table orders = tableEnv.scan("Orders"); Table result = orders.distinct();
|
操作 | 描述 |
---|---|
GroupBy 聚合 | 與SQL的GROUP BY類似,使用以下運(yùn)行的聚合運(yùn)算符對分組鍵上的行進(jìn)行分組,從而以組方式聚合行。val orders: Table = tableEnv.scan("Orders") val result = orders.groupBy('a).select('a, 'b.sum as 'd)
|
GroupBy Window 聚合 | 對分組窗口上的表,進(jìn)行分組和聚合,可能會按一個或者多個key進(jìn)行分組val orders: Table = tableEnv.scan("Orders") val result: Table = orders .window(Tumble over 5.minutes on 'rowtime as 'w) .groupBy('a, 'w) // group by key and window .select('a, w.start, 'w.end, 'b.sum as 'd) // access window properties and aggregate
|
Over Window 聚合 | 與SQL的OVER類似,根據(jù)前一行和后續(xù)行的窗口(范圍)為每行計(jì)算窗口聚合,參看over window獲取更多信息。val orders:Table = tableEnv.scan("Orders"); val result:Table = orders .window(Over .partitionBy("a") .orderBy("rowtime") .preceding("UNBOUNDED_RANGE") .following("CURRENT_RANGE") .as("w") .select("a, b.avg over w, b.max over w, b.min over w") // sliding aggregate
|
Distinct | 與SQL的DISTINCT類似,返回不重合的記錄。val orders:Table = tableEnv.scan("Orders"); val result:Table = orders.distinct();
|
Joins
操作 | 描述 |
---|---|
Inner Join | 與SQL的JOIN 類似,連接兩張表,兩張表必須具有不同的字段名稱,并且必須至少具有一個相等的連接謂詞,必須通過連接運(yùn)算符或使用where或filter運(yùn)算符來定義。Table left = tableEnv.fromDataSet(ds1, "a, b, c"); Table right = tableEnv.fromDataSet(ds2, "d, e, f"); Table result = left.join(right).where("a = d").select("a, b, e");
|
Left Outer Join | 與SQL的LEFT OUTER JOIN類似, |
Right Outer Join | 與SQL的RIGHT OUTER JOIN類似, |
Full Outer Join | 與SQL的FULL OUTER JOIN 類似, |
TableFunction Join | 與SQL的TableFunction Join類似 |
TableFunction Left Outer Join | 與SQL的TableFunction Left Outer Join類似 |