MaxCompute-ODPS重灌上陣 第三彈-複雜型別
MaxCompute(原ODPS)是阿里雲自主研發的具有業界領先水平的分散式大資料處理平臺, 尤其在集團內部得到廣泛應用,支撐了多個BU的核心業務。 MaxCompute除了持續優化效能外,也致力於提升SQL語言的使用者體驗和表達能力,提高廣大ODPS開發者的生產力。
MaxCompute基於ODPS2.0新一代的SQL引擎,顯著提升了SQL語言編譯過程的易用性與語言的表達能力。我們在此推出MaxCompute(ODPS2.0)重灌上陣系列文章
第一彈 – 善用MaxCompute編譯器的錯誤和警告
第二彈 – 新的基本資料型別與內建函式
第三彈 – 複雜型別
第四彈 – CTE,VALUES,SEMIJOIN
上次向您介紹了 新的基本資料型別與內建函式,這次向您介紹複雜資料型別
原ODPS也支援兩種複雜型別,ARRAY, MAP,但是有些場景下還是不夠用
-
場景1
我的專案裡,生成的一箇中間表,為了優化效能,裡面有一列最好是個陣列,因為如果把陣列打散,每行上存一個元素,會因為其他列的重複導致資料量爆炸。首先想從上游表中生成這個陣列,搜尋半天文件,發現唯一的方式是把源資料列先轉STRING,再用wm_concat聚合,再用split函式打散成ARRAY<STRING>,這樣原來型別資訊丟了,不過STRING似乎也能用,好,繼續。後面的運算有個地方需要取陣列最後一個元素,試圖用陣列下標配合size函式,my_array[size(my_array)], 發現報告錯誤,下標必須是常量,可是我的陣列不是定長的,看看有沒有函式能反轉陣列呢?沒有!最後不得不放棄使用陣列。。。
-
場景2
我的任務是為每個廣告生成一個曲線,代表隨著廣告商的出價由低到高,預計的impression, click次數的曲線。最自然的表達是有個資料結構,裡面存著出價,impression次數,click次數。可是ODPS不支援這樣的用法,只好encode成一個字串,每次操作先編碼,再解碼。好麻煩,效率也很差,可是沒有辦法。。。
MaxCompute採用基於ODPS2.0的SQL引擎,大幅度改進了複雜型別並提供了配套的內建函式,基本解決了上述問題。
複雜型別的擴充
此文中採用MaxCompute Studio作展示 ( MaxCompute從2.8.0.2版本開始支援複雜型別,如果您的版本不夠新,請升級到最新版本 )。
首先,安裝MaxCompute Studio,匯入測試MaxCompute專案,建立工程,建立一個新的MaxCompute指令碼檔案, 如下
執行後,可以在MaxCompute Studio的Project Explorer中找到新建立的表,察看錶的詳細資訊,並預覽資料,如下圖
可以看到MaxCompute支援ARRAY, MAP, STRUCT型別,並且可以任意巢狀使用。
MaxCompute Studio支援含新型別表資料的匯入匯出,可參考此ATA文章
- MaxCompute支援的複雜型別見下表
型別 | 定義示例 | 構造示例 | 訪問示例 |
---|---|---|---|
ARRAY |
array<int> , array<struct<a:int, b:string>>
|
array(1, 2, 3) , array(array(1, 2), array(3, 4))
|
a[1] , a[x][y]
|
MAP |
map<string, string> ,map<tinyint, array<string>>
|
map("k1", "v1", "k2", "v2") ,map(1Y, array(`a`, `b`), 2Y, array(`x`, `y`))
|
m[`k1`] ,m[2Y][id]
|
STRUCT |
struct<x:int, y:int> ,struct<a:array<int>, b:map<int, int>>
|
named_struct(`x`, 1, `y`, 2) , named_struct(`a`, array(1, 2), `b`, map(1, 7, 2, 8)
|
s.x , s.b[1]
|
複雜型別構造與操作函式
返回型別 | 簽名 | 註釋 |
---|---|---|
MAP<K, V> | map(K key1, V value1, K key2, V value2, …) | 使用給定key/value對建立map, 所有key型別一致,必須是基本型別,所有value型別一致,可為任意型別 |
ARRAY<K> | map_keys(Map<K, V> m) | 將引數中的map的所有key作為陣列返回,輸入NULL,返回NULL |
ARRAY<V> | map_values(MAP<K, V> m) | 將引數中的map的所有value作為陣列返回,輸入NULL,返回NULL |
int | size(MAP<K, V>) | 取得給定MAP元素數目 |
TABLE<K, V> | explode(MAP<K, V>) | 表生成函式,將給定MAP展開,每個key/value一行,每行兩列分別對應key和value |
ARRAY<T> | array(T value1, T value2, …) | 使用給定value構造ARRAY,所有value型別一致 |
int | size(ARRAY<T>) | 取得給定ARRAY元素數目 |
boolean | array_contains(ARRAY<T> a, value v) | 檢測給定ARRAY a中是否包含v |
ARRAY<T> | sort_array(ARRAY<T>) | 對給定陣列排序 |
ARRAY<T> | collect_list(T col) | 聚合函式,在給定group內,將col指定的表示式聚合為一個陣列 |
ARRAY<T> | collect_set(T col) | 聚合函式,在給定group內,將col指定的表示式聚合為一個無重複元素的集合陣列 |
TABLE<T> | explode(ARRAY<T>) | 表生成函式,將給定ARRAY展開,每個value一行,每行一列對應相應陣列元素 |
TABLE (int, T) | posexplode(ARRAY<T>) | 表生成函式,將給定ARRAY展開,每個value一行,每行兩列分別對應陣列從0開始的下標和陣列元素 |
STRUCT<col1:T1, col2:T2, …> | struct(T1 value1, T2 value2, …) | 使用給定value列表建立struct, 各value可為任意型別,生成struct的field的名稱依次為col1, col2, … |
STRUCT<name1:T1, name2:T2, …> | named_struct(name1, value1, name2, value2, …) | 使用給定name/value列表建立struct, 各value可為任意型別,生成struct的field的名稱依次為name1, name2, … |
TABLE (f1 T1, f2 T2, …) | inline(ARRAY<STRUCT<f1:T1, f2:T2, …>>) | 表生成函式,將給定struct陣列展開,每個元素對應一行,每行每個struct元素對應一列 |
在UDF中使用複雜型別
原ODPS不支援在UDF中訪問任何複雜型別。MaxCompute Java UDF支援所有複雜型別,Python UDF也會在不久的將來支援。
JAVA UDF中複雜型別的表示方法
ODPS的UDF分為三大類:UDF,UDAF,UDTF,其中:
- UDAF和UDTF通過@Resolve annotation來指定sinature,在MaxCompute 2.0上線後,使用者將可以在Resolve annotation中。如
@Resolve("array<string>,struct<a1:bigint,b1:string>,string->map<string,bigint>,struct<b1:bigint, b2:binary>")
- UDF通過evaluate方法的signature來對映UDF的輸入輸出型別,此時參考MaxCompute型別與java型別的對映關係。其中array對應
java.util.List
, map對應java.util.Map
,struct對應com.aliyun.odps.data.Struct
。需要注意的是,com.aliyun.odps.data.Struct
從反射是看不出field name和field type的,所以需要用@Resolve
annotation來輔助,即如果需要再UDF中使用struct,要求在UDF class上也標註上@Resolve
註解,這個註解只會影響引數或返回值中包含com.aliyun.odps.data.Struct
的過載。目前class上只能提供一個@Resolve
annotation,因此一個UDF中帶有struct 引數或返回值的過載只能有一個。這個是目前的一個限制,我們正在改進,後續將會取消這一限制。
實際用例
如以下程式碼,定義了一個有三個overloads的UDF,其中第一個用了array作為引數,第二個用了map作為引數,第三個用了struct。由於第三個overloads用了struct作為引數或者返回值,因此要求必須要對UDF class打上@Resolve
annotation,來指定struct的具體型別。
@Resolve("struct<a:bigint>,string->string")
public class UdfArray extends UDF {
public String evaluate(List<String> vals, Long len) {
return vals.get(len.intValue());
}
public String evaluate(Map<String,String> map, String key) {
return map.get(key);
}
public String evaluate(Struct struct, String key) {
return struct.getFieldValue("a") + key;
}
}
使用者可以直接將複雜型別傳入UDF中:
create function my_index as `UdfArray` using `myjar.jar`;
select id, my_index(array(`red`, `yellow`, `green`), colorOrdinal) as color_name from colors;
小節
MaxCompute豐富了複雜型別的支援,可以更好的適應各種應用場景。MaxCompute仍將在相容性,表達能力等多方面持續改進型別系統。從本系列的下一篇起,開始介紹MaxCompute在SQL語言其他方面的改進!
相關文章
- MaxCompute-ODPS重灌上陣 第一彈-善用MaxCompute編譯器的錯誤和警告編譯
- 簡單型別與複雜型別及原型鏈型別原型
- javascript複雜型別如何傳參JavaScript型別
- CXF--處理複雜型別型別
- hive複雜資料型別的用法Hive資料型別
- Java複雜資料型別用法 (轉)Java資料型別
- 第 10 節:複合型別 1.4 氣泡排序與陣列去重型別排序陣列
- 陣列(引用型別)複製問題陣列型別
- Oracle動態SQL引數支援複雜型別OracleSQL型別
- 第 10 節:複合型別 1:陣列型別陣列
- PHP 陣列排序(複雜字串)PHP陣列排序字串
- Redis基礎、常用型別介紹、時間複雜度Redis型別時間複雜度
- TypeScript魔法堂:函式型別宣告其實很複雜TypeScript函式型別
- 複雜性Complex與複雜Complicated區別 - Sonja
- 第三十一天 js實現的複雜驗重 (不推薦使用)JS
- Typescript複雜型別的宣告:寫一個工具函式庫TypeScript型別函式
- C語言中的複雜資料型別,你掌握了哪些?C語言資料型別
- XML Schema 複雜元素型別詳解:定義及示例解析XML型別
- 使用ajax請求傳送複雜的json資料型別,並解決fastjson解析複雜的json資料型別的問題JSON資料型別AST
- iOS 模型陣列去重複iOS模型陣列
- java陣列中重複元素的去重Java陣列
- 在asp.net AJAX客戶端使用複雜資料型別ASP.NET客戶端資料型別
- 物件導向程式設計:Java複雜資料型別用法(轉)物件程式設計Java資料型別
- JavaScript 刪除陣列重複元素JavaScript陣列
- 陣列中重複的數字陣列
- es6陣列去重複陣列
- JavaScript陣列刪除重複元素JavaScript陣列
- TypeScript 陣列型別TypeScript陣列型別
- js陣列型別JS陣列型別
- 重複程式碼(克隆程式碼)的幾個概念與型別型別
- LONG型別複製型別
- MVC驗證11-對複雜型別使用jQuery非同步驗證MVC型別jQuery非同步
- RAC重構型別型別
- Redis的雜湊型別(Hash)Redis型別
- Scala結構型別與複合型別解析型別
- 面試必問的陣列去重複面試陣列
- PHP陣列去除空白或重複元素PHP陣列
- javascript去掉陣列中重複的值JavaScript陣列