首頁
>
資源
>
會議活動

Apache IoTDB 樹表雙模型功能詳解:直播回顧(下)

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)決定了它一定是一個表模型。

表模型直播回顧圖19-20240303.png

(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ù)。物理上面是這樣組織的,會比邏輯視圖要稍微復雜一點。

表模型直播回顧圖20-20240303.png

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

表模型直播回顧圖21-20240303.png

(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 信息給帶上了,讓用戶去指定,這樣我們就可以達到自動建表和自動擴展列的效果。

表模型直播回顧圖22-20240303.png

(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ù)過期自動刪除的策略。

表模型直播回顧圖23-20240303.png

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

表模型直播回顧圖24-20240303.png

還有一種方式是我直接刪除整個設備,包括這個設備的元數(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 刪除會更加徹底。

表模型直播回顧圖25-20240303.png

(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 的合并進行消除。

表模型直播回顧圖26-20240303.png

下面是兩個需要間接更新的列,也就是 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)間接的更新。

表模型直播回顧圖27-20240303.png

(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ù)庫里面你給某一些列建了主鍵索引,它只會在寫入/更新/刪除的時候有一些約束條件,在查詢的時候這些主鍵索引的地位跟其他列是一樣的。

表模型直播回顧圖28-20240303.png

聚合查詢

接下來是聚合查詢,比如想獲得某張表某段時間內(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 就不會返回了。

表模型直播回顧圖29-20240303.png

降采樣查詢

講完聚合查詢,其實降采樣一定程度上也是屬于聚合查詢的一種,只是它在時序場景里面比較特殊,是在 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ù)。

表模型直播回顧圖30-20240303.png

這里解釋了一下 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 也不會幫你填充。

表模型直播回顧圖31-20240303.png

空值填充

剛剛提到表模型的空值填充,也就是通過 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 功能在表模型里面一個加強。

表模型直播回顧圖32-20240303.png

結(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)計出來會是兩行。

表模型直播回顧圖33-20240303.png

多表聯(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é)果了。

表模型直播回顧圖34-20240303.png

子查詢

子查詢功能也在表模型里面引入了。子查詢就是嵌套在另一個 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 的異常行,這個在查詢里面還是非常有用的。

表模型直播回顧圖35-20240303.png

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)了,也會在前面的版本去放出來。

表模型直播回顧圖36-20240303.png

更多內(nèi)容推薦:

? 了解如何使用 IoTDB 企業(yè)版

? 了解更多 IoTDB 應用案例