2 月 26 日面向 Apache IoTDB 樹表雙模型的功能特性、適用場景、建模選擇和未來規(guī)劃,田原同學通過直播進行了全面解答。以下為直播講稿(下),干貨滿滿,建議收藏????
02 表模型功能介紹
跟大家簡單介紹了背景之后,會再花一些時間去介紹一下怎么創(chuàng)建表模型的連接,怎么進行表跟數(shù)據(jù)庫的管理,還有增刪改查這四個常見的操作,可能查詢會講得久一點。
(1)創(chuàng)建表模型連接
首先是創(chuàng)建表模型的連接,我們有 client,就是我們自己提供的命令行,還有 JDBC,還有 Java 的這些 SDK,那它們的指定方式分別是什么呢?就是剛剛也提到過的 -sql_dialect。我們是把 table 和 tree 作為兩種視圖、兩種方言,去對同一個數(shù)據(jù)進行操作的,所以是通過 -sql_dialect table 的方式去指定連接用哪一個視圖去看你的這份數(shù)據(jù)。這個參數(shù)是可選的,這是為了以前的樹模型用戶做兼容,因為以前樹模型用戶是不需要指定這個的,所以你不指定的話,我們還是默認為你是樹模型用戶,你只有指定了這個之后,你才是一個表模型用戶。連進來之后,我們在 CLI 里面也可以執(zhí)行 SET SQL_DIALECT=TABLE/TREE 進行模型切換,或者執(zhí)行 SHOW CURRENT_SQL_DIALECT 去看當前的模型到底是樹,或者當前的語法到底是樹還是表。
JDBC 也是一樣的,它通過這個問號后面的 sql_dialect=table 去決定是 table 還是 tree,你不指定的話默認還是tree。跟樹模型不太一樣的是我們在這個端口后面還多增加了一級數(shù)據(jù)庫,對于表模型來講,你可以直接連到這個默認數(shù)據(jù)庫的空間,也可以執(zhí)行 SET SQL_DIALECT=TABLE/TREE 去進行模型的切換。
Java 的 SDK 就比較簡單了,我們重新為表模型抽象了一套接口,也就是我在用表試圖進行寫入和查詢的時候,它是用 ITableSessionPool 和 ITableSession 這兩個組件進行讀寫的,原來的 Session 或者 SessionPool 就服務于樹模型了。通過這樣做區(qū)分之后,它就不支持模型切換了,因為本身接口名字就已經(jīng)決定了它一定是一個表模型。

(2)數(shù)據(jù)庫&表管理
接下來一塊是我們的數(shù)據(jù)庫和表管理。剛剛說不同的模型,它在空間上面目前是分離的,所以一個數(shù)據(jù)庫它要么屬于樹模型,要么屬于表模型,取決于是在樹模型連接下面創(chuàng)建的,還是表模型連接下面創(chuàng)建的。一個 DATABASE 下面可以有多張表,我們的時間分區(qū)也只能指定到 DATABASE 層級,就是我要按多少天或者默認一周進行分區(qū)。數(shù)據(jù)副本也只能指定到數(shù)據(jù)庫層級,但是 TTL 是可以指定到表級別的。
對于這個邏輯視圖而言,左下角的邏輯視圖,用戶看到的就是一個 IoTDB 下面可以有多個 DATABASE,每個 DATABASE 下面有多張表。但是到 IoTDB 的物理文件組織上面,data 目錄下面可能先分了順序/亂序,順序下面分了不同的 DATABASE。DATABASE 到表之間還有兩層,這兩層就是我們做寫入并發(fā)或者查詢并發(fā)的時候,IoTDB 在內(nèi)部幫我們做了 region,就是我們的一個并發(fā)單元。這個 region 下面會有剛剛說的指定時間分區(qū),每個時間分區(qū)數(shù)據(jù)是會被放在一個文件夾下面的,一個時間分區(qū)下面會有多個 table 的數(shù)據(jù)。物理上面是這樣組織的,會比邏輯視圖要稍微復雜一點。

那我們怎么去創(chuàng)建一個表呢?創(chuàng)建語法其實也跟關(guān)系型數(shù)據(jù)庫比較類似,唯一的區(qū)別就是我們多了一個列類別,我在這邊用顏色標出來的,包括標簽列、屬性列、TIME 列和測點列,我們要在數(shù)據(jù)類型后面去指定到底這一列屬于哪個類別。寫出來的這個創(chuàng)建示例以 region、car_id 作為 TAG 列,model,就是屬于哪一種車型作為屬性列,速度還有溫度作為 FIELD 列。

(3)數(shù)據(jù)寫入
表模型的功能首先是數(shù)據(jù)寫入。數(shù)據(jù)寫入目前是支持 CLI 里面的 SQL、JDBC SQL、Java SDK、Python SDK 以及 REST API SDK。
大家可能會比較好奇我應該用哪一個?首先對于 CLI 里面的 SQL,它肯定不適合在生產(chǎn)里面去寫數(shù)據(jù),因為它只適合我們測試的時候去隨時構(gòu)造測試的數(shù)據(jù)集,并且隨時去查看這個數(shù)據(jù)集對不對。JDBC SQL 是一個標準的接口協(xié)議,適合跟第三方的生態(tài)做集成,比如 DBeaver。目前不太推薦在生產(chǎn)里面直接用 JDBC 進行寫入,因為它的寫入性能相較于下面幾個會低一些。Java SDK 就是我們原生提供的 Java 的 API,它的性能非常高,我們推薦生產(chǎn)里面直接用;Python 也是,這個取決于你的業(yè)務到底是用 Java 寫的還是用 Python 寫的,可以選擇不同的 SDK 進行寫入。REST API 的性能也比 JDBC SQL 要高,但是它比 Java 和 Python 這種原生的數(shù)據(jù)接口要稍微低一些,它適合一些業(yè)務開發(fā)語言 SDK 還沒有提供的時候使用,比如 C、C++、Go 語言還沒提供,可以用 REST API 進行寫入。
表格中也對各類寫入方式的便捷程度、性能以及是否支持自動建表和自動擴展列做了一個對比,大家也可以根據(jù)這個表格去決定自己到底要用什么。這里為什么有一些是不支持的,比如 CLI SQL 和 JDBC SQL 為什么不支持自動建表和自動擴展列?因為我們的寫入 SQL 是向標準 SQL 做兼容的,我們不會在 insert 里面去加入指定這一列的功能,因為只有在 insert 的 SQL 里面去指定了我插的這一列是什么類別,它到底是 TAG 列還是屬性列還是測點列,我才能知道該怎么樣去建這個表,但是標準 SQL 里面是不會給你指定這些的,所以 CLI SQL 和 JDBC SQL 里面就不支持自動建表和自動擴展列了。但是原生的 Java SDK、Python SDK 和 REST API,我們在設計的時候就把這些 schema 信息給帶上了,讓用戶去指定,這樣我們就可以達到自動建表和自動擴展列的效果。

(4)數(shù)據(jù)刪除
寫入講完了,下面是刪除。IoTDB 的刪除分成三種,一種是自動刪除過期數(shù)據(jù),一種是手動刪除數(shù)據(jù),一種是手動刪除設備,這三者有什么區(qū)別呢?自動刪除過期數(shù)據(jù)其實就是 TTL,這個適用場景是我們某一個 table 里面的數(shù)據(jù)只需要保留一周,那我就可以在建表的時候去指定這樣一個 TTL,CREATE TABLE with (TTL=毫秒級別的精度)。你如果不指定的話,TTL 默認就是 Infinity 無窮大,就是永不過期的。
那創(chuàng)建表之后我能不能修改呢?比如我一開始創(chuàng)建的時候忘了指定了,我也不想 TTL 無窮大,那可以通過 ALTER TABLE 這種標準的語法去 set properties,讓 TTL 等于這個值。同樣的,這個精度也是毫秒,這是我們設置數(shù)據(jù)過期自動刪除的策略。

第二種就是手動刪除,場景是因為我錯誤地錄入了某一些數(shù)據(jù),我想要手動地刪掉某一個設備某一段時間內(nèi)的所有數(shù)據(jù),可以用 DELETE FROM,然后在 where 里面指定設備的過濾條件和時間范圍。當前有一些限制條件,大家可以看一下下面的這個表為例,如果想刪掉 car1 的 time 在 3-4 之間的數(shù)據(jù),我就可以寫一個語句,時間戳 3/4 的數(shù)據(jù)就會被刪掉了。

還有一種方式是我直接刪除整個設備,包括這個設備的元數(shù)據(jù),這個跟 DELETE FROM 有什么差別呢?DELETE FROM 不指定時間范圍也可以把這個設備的所有數(shù)據(jù)都刪掉,但 DELETE DEVICES 它刪得更徹底,它會刪掉一些元數(shù)據(jù),也就是設備標識和設備的靜態(tài)屬性,DELETE DEVICES 之后,運行 SHOW DEVICES FROM TABLE,是看不到這個設備的。比如我想要刪掉 car2 這個車的所有數(shù)據(jù),刪掉之后它連 SHOW DEVICES 都不展示了,但是我如果用 DELETE FROM 這個語法,它只會刪除數(shù)據(jù),也就是會把 car2 的所有數(shù)據(jù)刪掉,但是 car2 這個車還是保留著的,所以我在 SHOW DEVICES 的時候仍然能看到 car2 這個車的這條記錄。這是它們倆的一個區(qū)別,DELETE DEVICES 刪除會更加徹底。

(5)數(shù)據(jù)更新
第三個功能就是數(shù)據(jù)更新,IoTDB 有一些列是可以直接更新的,有一些列是需要間接的更新的。
我們可以直接更新的列有哪些呢?剛剛說了列類別分為四種,TIME 列、TAG 列、FIELD 列、ATTRIBUTE 列,當中的 ATTRIBUTE 列(屬性列)和 FIELD 列(測點列)是能夠直接被更新的。
ATTRIBUTE 列剛剛說了是一個設備的靜態(tài)屬性,它可以直接用 UPDATE 語句更新,這個跟標準關(guān)系 SQL 定義的 UPDATE 是一樣的。update 某一個 table 里面的某一個屬性鏈,我要把 car1 的 su7 更新成 yu7,就可以直接用 UPDATE 語句進行更新。它的代價是很低的,因為我們之前說過,雖然看上去有多少行就應該改多少行,但是其實在物理存儲里面只存了一行,所以更新代價是很低的。
FIELD 列,就是某一設備某一時刻采集的值也是可以直接更新的,可以通過寫入重復的時間戳和需要更新的值去做。比如我要去更新 car1 的時間戳為 1 的速度改成了 15 千米/小時,原來是 10 千米/小時,那我就直接 INSERT INTO table,指定 car1、我想要修改的時間和我想要修改的列的值為 15.0,它自動就更新掉了。當然這個修改的代價跟你更新的行數(shù)有關(guān),你更新一行就相當于往數(shù)據(jù)庫里面多插了一行,這個數(shù)據(jù)會變成亂序數(shù)據(jù),可能會對查詢性能產(chǎn)生一定的影響,但這個影響會隨著 IoTDB 的合并進行消除。

下面是兩個需要間接更新的列,也就是 TAG 列和 TIME 列,它們的定義上是不可以修改的,但是我們可以通過組合 SELECT、INSERT、DELETE 的方式間接地更新。比如我想把 car1 改名為 car2,我們需要把原設備的所有數(shù)據(jù)查出來,然后再分批寫入到我們想要改名的新數(shù)據(jù)當中。比如原始數(shù)據(jù)叫 car1,我先把它查出來,查出來的所有數(shù)據(jù)再 INSERT 分批寫入要重命名的 car2,之后再用 delete devices 把所有設備、元數(shù)據(jù)、數(shù)據(jù)都刪除,這樣就實現(xiàn)了 car1 到 car2 的改名功能。
第二個就是 TIME 列,更新時間戳理論上是不行的,但同樣可以通過這種組合的方式達到。我們把原來時間戳那一行查出來,比如要把 2 改成 3,那我把 2 先查出來,然后 INSERT 一條 3 的數(shù)據(jù)行,這是一個中間狀態(tài)。INSERT 成功之后,我們再把 2 這一行 delete 掉,用 delete from 就能做到,這樣也能實現(xiàn)間接的更新。

(6)數(shù)據(jù)查詢
主要的時間留給查詢,查詢內(nèi)容是比較豐富的,我也列了一些典型的查詢場景。
原始數(shù)據(jù)查詢
首先,第一個查詢場景是原始數(shù)據(jù)查詢。這個場景一般是我們想要去獲取某一個設備下某段時間內(nèi)的原始數(shù)據(jù),可以在 where 子句里面去指定設備過濾條件及時間范圍,比如 select from table1,然后指定 device_id=’d1’ ,time 在 1-2,這樣我就可以得到原來這張表里面的這兩行數(shù)據(jù)。
那如何獲取多個設備某段時間內(nèi)的原始數(shù)據(jù)?device_id IN 或者 device_id=’d1’ or device_id=’d2’,用 in 和用 or 去連接兩個等號是沒什么區(qū)別的,可以得到 d1 和 d2 在時間范圍 1-2 里面的 4 行數(shù)據(jù)。
如果想獲得一個設備某段時間內(nèi)某個指標滿足某個條件的數(shù)據(jù),可以前面的過濾范圍不動,再加上 s1 ≥2。
如果想獲取某一個設備某段時間內(nèi)所有數(shù)據(jù)的時間戳,也就是我只關(guān)心時間戳,有可能我這個數(shù)據(jù)特別大,數(shù)據(jù)層是一個 binary 對象,我只關(guān)心我在哪些時間點采集了這些數(shù)據(jù),并不關(guān)心這個數(shù)據(jù)是什么,這個時候就可以在 select 里面僅寫 time 列查到這些數(shù)據(jù)。這個在表模型其實沒有辦法做到,因為表模型里面默認是必須帶有 time 的,并且這個 time 就不需要去寫。
這塊也跟大家說一下,表模型建表或?qū)懭氲臅r候,列的類別分了四類,但是表模型的查詢當中,邏輯上已經(jīng)不區(qū)分列的類別了。查詢里面 TAG/FIELD/ATTRIBUTE/TIME 都是一樣的、普通的列,TIME 就是一個普通的數(shù)據(jù)類型為 TIMESTAMP 的列。當然,在物理執(zhí)行上面這些列的篩選效率還是有區(qū)別的,但這個是內(nèi)部執(zhí)行的事,從用戶的邏輯視圖上面來看,這些列的地位都是等價的。就像關(guān)系型數(shù)據(jù)庫里面你給某一些列建了主鍵索引,它只會在寫入/更新/刪除的時候有一些約束條件,在查詢的時候這些主鍵索引的地位跟其他列是一樣的。

聚合查詢
接下來是聚合查詢,比如想獲得某張表某段時間內(nèi)寫入的總行數(shù),我們就可以用 count(*),這也是一個標準 SQL 的查詢方式,也只有 count 這個聚合函數(shù)可以用 (*),這一塊是跟樹模型不太一樣的。我能查出來 1-2 這個時間范圍這張表有 4 行數(shù)據(jù),所以它 total_line 是 4。
我想獲取每一個設備的最新一行數(shù)據(jù),那就可以用 last_by 聚合函數(shù),配合 where 子句里面指定設備的過濾條件。比如我要 device1 的最新數(shù)據(jù),語句就寫最后一個時間戳以及對應最后一行的 s1 last by time、s2 last by time,讀出來的結(jié)果就能把最后一行給篩選出來。
如果想獲得每一個設備的最新數(shù)據(jù),該怎么做呢?我可以 from table1 不指定 where 的設備過濾條件,在語句中指定 group by device_id,按 device 分組之后得到每一個分組的最后一行。
聚合查詢還有很多過濾方式,比如想去獲得某一個指標某段時間內(nèi)的平均值,并只想返回平均值 > 2 的設備,我們可以先按設備分組之后去求它的平均值,也就是 avg,得到 d1 的平均值是 2,d2 的平均值是 20。然后二次過濾用 having 語句去要平均大于 2 的設備這一列,過濾出來只會返回 d2,d1 就不會返回了。

降采樣查詢
講完聚合查詢,其實降采樣一定程度上也是屬于聚合查詢的一種,只是它在時序場景里面比較特殊,是在 time 上面的分組。降采樣是什么概念呢?可能我想獲取某一個設備某個指標某段時間內(nèi)的平均值,需要在時間上做一個降采樣。跟在樹模型里面的實現(xiàn)方式不一樣,我們是做了一種跟標準關(guān)系 SQL 兼容的,通過 data_bin 函數(shù)加上標準的 group by clause 去達到這個概念。
data_bin 就是將一個時間戳列的所有數(shù)據(jù)全部規(guī)整到對應時間分段的起點并返回,有點繞口,其實大家看這個圖能比較明白。比如我想按小時對數(shù)據(jù)進行規(guī)整,那 0:30 規(guī)整到小時應該是凌晨0點的時刻,1:30 應該規(guī)整到 1 點。規(guī)整完之后這些數(shù)據(jù)是一個標準的標量函數(shù),它會得到的結(jié)果就是全部按小時把數(shù)據(jù)給對齊了。有了對齊的數(shù)據(jù)之后,我們就直接可以用 group by 去達到一個真正降采樣的效果。我如果只需要某個范圍某個設備的每小時的平均溫度,那先把時間按照一小時做規(guī)整,根據(jù)過濾條件之后,我應該得到 d1 的這四行數(shù)據(jù),得到規(guī)整的時間軸之后再執(zhí)行 group by,再在每一個分組里面求平均溫度值就可以了。
大家熟悉樹模型的可能會看到,我們在 where 條件里面指定的是 ≥4 點、<12 點,但是中間其實有 9/10 點鐘沒有展示出來,原來用樹模型的時候是能展示出來的。這是因為我們用這套標準語法去寫的時候,降采樣結(jié)果集里面本來就沒有這些行,結(jié)果就不會展示,但后面我們有一個特殊的語法去幫助解決這件事情,把一些我們并沒有數(shù)據(jù)的時間區(qū)間填充上。
在講這個語法之前,我們可以看一下,剛剛是針對一個設備的,現(xiàn)在是每一個設備。其實就是在 group by 的時候,在 where 里面不指定設備的過濾條件,在 group by 里面加一個 device_id 的分組。這樣我就把每一個 device 的數(shù)據(jù)分別按小時規(guī)整,再按設備以及對應的小時去進行分組,得到這個數(shù)據(jù)。

這里解釋了一下 data_bin 和 avg 函數(shù)怎么樣達到降采樣的效果。如果我們需要獲取所有設備某個數(shù)據(jù)時間范圍內(nèi)每小時的平均溫度,也就是我不關(guān)注每一個設備內(nèi)部了,我要關(guān)注整個表的概念,或者我關(guān)注更上一個層級的集團下所有設備總體的平均值,我在做的時候就不需要 group by device_id 了,規(guī)整之后有很多設備的 8 點鐘數(shù)據(jù)就會被最后平均到一行上面了。
這個就是我剛剛跟大家提到的 gapfill。之前用 group by+data_bin 的時候是不會展示沒有時間、沒有數(shù)據(jù)的時間分區(qū)的,加了 gapfill 尾綴后就能夠把這些空余的時間戳填上了。如果值有 null 的話,后面還會講到空值填充功能,可以把這些 null 值也填上。
gapfill 的限制在官網(wǎng)也能看到,我在這邊就不跟大家贅述了。唯一有一個特殊點是,如果整個數(shù)據(jù)都沒有的話,gapfill 也不會幫你填充。

空值填充
剛剛提到表模型的空值填充,也就是通過 gapfill 之后有一些 null 值希望去填充,和樹模型是一樣的,可以用 FILL 語句去填充,也支持三種相同的方式。剛剛的數(shù)據(jù)部分可以用前值填充的方式,但有 3 行沒有前面的值了,所以我指定 fill method previous 的時候沒有辦法填充前面這三行。
這里解釋一下表模型 FILL 語句跟樹模型的差異點。樹模型是在 ORDER BY 子句之后執(zhí)行 FILL 子句,而表模型是在 ORDER BY 子句之前執(zhí)行的,所以用 ORDER BY+FILL 的時候,樹模型和表模型的執(zhí)行結(jié)果可能是有一些差異的。在線性填充的時候有一個輔助列的概念,我要去填充 y,那怎么得到中間沒有值的 y 呢?我們要通過 x0 和 x1 去得到比例,然后用 y1 和 y0 去得到 y,所以 x 就成為帶填充列的輔助列。
原來在樹模型里面,我們的輔助列只能是 TIME 列,但是在表模型里面輔助列是能夠通過 TIME_COLUMN 去指定的。不指定的話我們會自動尋找你在 select 里面寫的第一個類型為 TIMESTAMP 的列作為輔助列,如果你指定了就可以用指定的那一列作為輔助列。比如有 TIME 和 event_time 這兩個不同的 TIMESTAMP,你可以指定用 event_time,也就是第 2 列作為輔助列。
還有一個差異,樹模型里面它分組內(nèi)的 FILL 是針對結(jié)果集的,也就是 align by device 做填充的時候可能會錯用前面的值,不會區(qū)分 d1/d2/d3,所以可能用 d2 的值去填充 d3 的數(shù)據(jù)。但是在表模型里面,你可以多指定一個分組內(nèi)填充,也就是指定分組鍵在 device_id 相同的里面進行填充,如果 device_id 不相同就不能填充了。所以指定 FILL_GROUP 為 2 的時候,d3 的數(shù)據(jù)就不會用 d2 的數(shù)據(jù)填充了。這個也是我們對于 FILL 功能在表模型里面一個加強。

結(jié)果集去重
下面是結(jié)果集排序 order by,這個應該是比較常見的功能。默認是降序去排,也就是最先寫的時間戳會在前面,我們可以通過 order by time desc 讓原始數(shù)據(jù)倒序地輸出,也可以去查最近 5 分鐘之內(nèi)設備最新的兩行數(shù)據(jù),還是按照時間軸去倒序排列并多加一個 limit 2。這里不限定 device 了,最新的兩行一個是 d2 的 time=3,一個是 d1 的 time=3,那到底是 d1 在前面還是 d2 在前面?如果你沒有指定 order by time desc , device_id,那這個順序是隨機的,有可能每次執(zhí)行的都不一樣。這個其實就是關(guān)于數(shù)據(jù)庫的語義了,因為你沒有針對這一列排序,所以它只會保證 TIME 列是有序的,但 TIME 列一樣的情況下,它的行順序是可以隨意調(diào)整的。
結(jié)果集去重也可以用 select distinct,比如想獲得某一個設備某段時間內(nèi)指標去重后的所有值,我們就可以寫 select distinct s1 from,可以看到去重之后只剩下兩行。distinct 遵循關(guān)系型數(shù)據(jù)庫 SQL 的語法,它也可以用在 aggregation function 里面。我們想統(tǒng)計 s1 已出現(xiàn)不同值的有多少行,那就可以用 count(distinct s1)。同樣,原來如果不寫 distinct,有多少個非 null 值都會統(tǒng)計出來,寫了 distinct 之后統(tǒng)計出來會是兩行。

多表聯(lián)合查詢
IoTDB 的樹模型只支持 TIME 列的 FULL OUTER JOIN 作為默認的 JOIN 方式,但是 IoTDB 在表模型里面其實是計劃支持所有標準類型的 JOIN,包括 INNER JOIN、LEFT JOIN、RIGHT JOIN 和 FULL OUTER JOIN,這個是原來樹模型就支持的。
在關(guān)系型數(shù)據(jù)庫標準代數(shù)定義的這四類之外,我們還會支持有時序語義特色的 ASOF JOIN。JOIN 其實就是多表的聯(lián)合查詢,當然也可以用這張表的子查詢結(jié)果集和這張表的另外一個子查詢結(jié)果集做表的自 JOIN。ASOF JOIN 是一個具有時序特色的 JOIN 方式,這個使用場景是什么呢?比如待分析的兩個時間序列,采集頻率是一樣的,但是因為網(wǎng)絡延遲或者上報誤差等原因,兩個序列的采集時間戳并不完全一致,會在一定時間范圍內(nèi)波動。那用戶就希望把兩個序列的數(shù)據(jù)根據(jù)最近的時間戳進行對齊顯示,比如 s1、 s2 都是秒級采集的,但毫秒部分有一點點誤差,用戶想做的就是兩個表的第一行、第二行、第三行接在一起,得到下表的結(jié)果。現(xiàn)有的四種 JOIN 方式都無法做到,因為只有一模一樣的時間戳數(shù)據(jù)才會連在一起,而 ASOF JOIN 是可以做到這個效果的,可以寫 table1 ASOF JOIN table2 ON table1.time ≤ table2.time,這樣就能得到我們的預期結(jié)果了。

子查詢
子查詢功能也在表模型里面引入了。子查詢就是嵌套在另一個 SQL 查詢當中的查詢語句,它的查詢結(jié)果會作為外層查詢的條件、數(shù)據(jù)源或者計算字段的一部分,子查詢可以出現(xiàn)在 SELECT、FROM、WHERE、HAVING 里面,是我們?nèi)ヌ幚韽碗s邏輯的常用工具。比如這邊舉的一個關(guān)聯(lián)查詢的例子,我們在內(nèi)部的子查詢里用到了外部的父級字段。
子查詢大體上也可以分為關(guān)聯(lián)查詢和非關(guān)聯(lián)查詢,IoTDB 目前的版本里面實現(xiàn)了非關(guān)聯(lián)查詢,在 SELECT、FROM、WHERE、HAVING 里面都可以出現(xiàn)。我們的關(guān)聯(lián)子查詢部分在 master 上,也就是最新分支上面已經(jīng)實現(xiàn)了,在后續(xù)的版本里很快就會釋放出來關(guān)聯(lián)子查詢的功能。
這里舉幾個典型的例子,比如子查詢用在 FROM 子句里面,我們想先求某個設備某段時間內(nèi)指標的平均值,然后獲得其中的最大平均值,那就可以先把每個設備查詢的平均值得到,查詢出來之后再在外層套一個 max 查詢,就能得到每一個設備里面 avg 的最大值。
還有一個例子,它可以用在我們的 where 子句,也就是標量子查詢里面,可以作為 s1= 的一個過濾條件。我們想得到一個設備某段時間內(nèi)某個指標最大值對應的所有行,有一種解法是先把最大值查到,再由客戶端去發(fā)一個查詢過來,s1=最大值 4 去查到,但是這樣用戶就要寫兩條 SQL 了。通過子查詢的方式,我可以直接寫 s1=某一個子查詢的標量結(jié)果,它是可以直接得到這個結(jié)果的。同樣的,比如我想查 >P99 所有的行,用分位數(shù)函數(shù)就是 s1 > select P99(s1) from table1,能查出來所有大于 P99 的異常行,這個在查詢里面還是非常有用的。

03 表模型 Roadmap
表模型的功能已經(jīng)大概介紹完了,下面跟大家簡單介紹一下表模型的 Roadmap。
我們打算 3 月上旬實現(xiàn)基本的讀寫功能,這其實已經(jīng) OK 了,包含在 beta 版本里面了,以及 beta 版本釋出、測試的時候,發(fā)現(xiàn)的一些 bug 的修復,這個會在 3 月上旬發(fā)出來一個 2.0.1 的正式版本。大概在 3 月下旬,我們想去發(fā)一個 2.0.2 的版本,去包含一些比較重要的功能更新,比如用戶和權(quán)限的管理、自定義函數(shù),包括自定義聚合函數(shù)和自定義標量函數(shù),還有 MQTT 的寫入?yún)f(xié)議,這個在一些工業(yè)場景里還是比較常見的,還有我們的系統(tǒng)表。
6 月下旬我們會去進一步擴展、補充查詢功能,包括窗口函數(shù)、動態(tài)表函數(shù),DBeaver 可視化工具,其他 SDK 的補齊,比如 C、C++、Go、C#、Rust 等等。在 9 月下旬,我們會支持完整的 JOIN 方式,比如剛剛提到的 ASOF JOIN,還有雙模型轉(zhuǎn)換的功能,關(guān)聯(lián)子查詢的功能可能會提前,因為 master 上面現(xiàn)在可能已經(jīng)有了。還有 SQL:2016 里面提到的行模式識別,也有可能在 9 月下旬的 2.0.4 版本釋出。最后,今年年底可能會對我們的 AI 能力、安全能力,包括模糊查詢和序列模式識別這些高階的功能做一個加強,會在 12 月下旬的 2.0.5 版本跟大家見面。
整體上我們是這么一個規(guī)劃,里面有一些功能如果提前實現(xiàn)了,也會在前面的版本去放出來。

更多內(nèi)容推薦:
? 了解如何使用 IoTDB 企業(yè)版
? 了解更多 IoTDB 應用案例