處理嵌套數據使用高階函數在SQL數據磚
2017年5月24日 在公司博客上
嵌套數據類型提供磚客戶和Apache引發用戶的強大處理結構化數據的方法beplay体育app下载地址。特別是,他們允許你把複雜的對象和數組一樣,地圖和內部結構的列。這可以幫助你模型數據在一個更自然的方式。雖然這個功能肯定是有用的,但它會很麻煩操縱數據的內部複雜的對象,因為SQL(火花)沒有原語來處理這些數據。此外,它是耗時的,non-performant和非平凡的。
由於這些原因,我們興奮地提供高階函數的SQL磚運行時的3.0版本,允許用戶高效地創建函數,在SQL操作基於數組的數據。高階函數是一個簡單的擴展SQL操作嵌套數組等數據。例如,變換
下麵表達式顯示了如何添加一個數字數組中每個元素:
在這篇文章中,我們將討論以前的方法嵌套SQL數據操作,其次是高階函數語法我們已經介紹了磚。
檢查為什麼Lakehouse的數據是你的下一個數據倉庫的電子書發現磚的內部運作Lakehouse平台。Beplay体育安卓版本
過去的方法
在我們介紹數組操作的新語法之前,讓我們首先討論當前操作這類數據在SQL方法:
- 內置函數(有限的功能)
- 解壓縮數組為單個行,應用你的函數,然後重新打包成數組(很多步驟,因此效率低下)
- udf(不是一般的或有效)
我們將探討這些獨立,這樣你可以理解為什麼數組操作是很困難的。讓我們開始下麵的表的模式(包括看到筆記本容易運行的代碼)。
根| - - -關鍵:長(可空=假)|——價值觀:數組(可空=假)| |——元素:整數(containsNull =真正的)|——nested_values:數組(可空=假)| |——元素:數組(containsNull =假)| | |——元素:整數(containsNull =真正的)
內置函數
火花SQL確實有一些內置函數用於操作數組。例如,您可以創建一個數組,它的大小,得到特定元素,檢查如果數組包含一個對象,數組進行排序。SQL還支持發電機(火花爆炸
,pos_explode
和內聯
),允許您將輸入行數組元素,和collect_list
聚合。這個功能可以滿足您的需要對某些任務,但它是複雜的做任何事不平凡的,如計算每個數組元素的一個定製的表達。
解包和重新打包
非平凡的操作的常用的方法是“打開和重新打包”方法。這是一個“火花SQL原生”的方式解決問題,因為你不需要編寫任何自定義代碼;您可以簡單地編寫SQL代碼。打開和重新打包方式是通過以下步驟:
- 使用
側麵圖爆炸
平數組,輸入行與數組中的每個元素相結合; - 應用一個給定的變換,在這個例子中
值+ 1
爆炸數組中的每個元素;和 - 使用
collect_list
或collect_set
創建一個新的數組。
我們可以看到一個這樣的例子在SQL代碼如下:
選擇鍵,值,collect_list (價值+1)作為values_plus_one從nested_data橫向視圖爆炸(值)T作為價值集團通過鍵,值
雖然這種方法肯定奏效,它有一些問題。首先,你必須絕對確保密鑰用於分組是獨一無二的,否則結果將是不正確的。第二,沒有保證排序數組的火花SQL。指定一個操作,需要一個特定的順序幾乎保證不正確的結果。最後,生成的火花SQL計劃可能會非常昂貴。
用戶定義的函數(udf)
最後,我們可以編寫自定義udf來操作數組的數據。udf必須定義如何遍曆一個數組,我們如何處理單個元素。讓我們看看一些基本例子在Python和Scala。
(code_tabs)
從pyspark.sql.types進口IntegerType從pyspark.sql.types進口ArrayTypedefadd_one_to_els(元素):返回[el +1為埃爾在元素)spark.udf.register (“plusOneIntPython”、add_one_to_els ArrayType (IntegerType ()))
defaddOneToElements(元素:Seq [Int])=元素。地圖(= >元素+1)火花。udf。注冊(“plusOneInt”addOneToElements (_:Seq [Int]):Seq [Int])
[/ code_tabs]
一旦注冊,我們可以使用這些函數來操縱我們的數據在火花SQL。
選擇鍵,值,plusOneInt (值)作為values_plus_one,plusOneIntPython (值)作為values_plus_one_py從nested_data
這種方法有一些優勢在過去的版本:例如,它維護元素順序,與包裝和重新打包方法。然而,它有兩個主要缺點。首先,你必須編寫函數在其他語言比SQL和注冊之前運行。第二,數據序列化到Scala和Python可以非常昂貴,減緩udf火花的SQL內置的優化處理。
我們的方法:高階函數
從上麵的例子中,觀察到傳統的方式在SQL繁瑣操作嵌套數據。為此,我們建立了一個簡單的解決方案在磚:高階函數的SQL。
我們的解決方案引入了兩個SQL函數編程結構:高階函數和匿名(λ)函數。這些共同允許您定義的函數操作數組在SQL。的高階函數,如變換
,和一個數組lambda函數從用戶運行。然後調用這個lambda函數數組中的每個元素。
一個簡單的例子:變換
讓我們說明前麵的概念與我們之前的轉換的例子。在這種情況下,高階函數,變換
將遍曆該數組,相關的lambda函數適用於每一個元素,並創建一個新數組。lambda函數,元素+ 1
,指定每個元素是如何被操縱的。
選擇鍵,值,變換(值,價值- - - - - ->價值+1)作為values_plus_one從nested_data
清晰的說明,轉換變換(價值觀、價值- >價值+ 1)
有兩個組件:
變換值. .)
是高階函數。這需要一個數組和一個匿名函數作為輸入。內部變換將照顧設置一個新數組,每個元素應用匿名函數,分配結果輸出數組。- 的
值- >值+ 1
是一個匿名函數。的功能分為兩個組件分離- >
符號:
一個。參數列表。在這種情況下,我們隻有一個論點:價值。我們也支持多個參數通過創建一個逗號分隔的參數列表包圍括號,例如:(x, y) - > x + y
。
b。身體。這是一個表達式,可以使用參數和外部變量計算新值。在這種情況下,我們的值加1的論點。
捕獲變量
我們還可以使用其他比lambda函數中的參數變量;這就是所謂的捕捉。我們可以用變量定義在頂層,或在中間lambda函數定義的變量。例如,下麵的改變增加了關鍵(頂級)變量值數組中的每個元素:
選擇鍵,值,變換(值,價值- - - - - ->價值+鍵)作為values_plus_key從nested_data
嵌套調用
有時數據是深度嵌套。如果你想改變這樣的數據,你可以可以使用嵌套的lambda函數。下麵的例子將整數數組,數組並添加關鍵(頂級)列和中間數組的大小嵌套數組中的每個元素。
選擇鍵,nested_values,變換(nested_values值- - - - - ->變換(值,價值- - - - - ->價值+關鍵+大小(值)))作為new_nested_values從nested_data
支持功能
我們有下麵的高階函數添加到3.0版本的磚運行時。
變換(數組
、功能
):數組
這產生一個數組
通過應用函數
一個輸入的每個元素數組
。
注意,函數式編程操作映射。這已經被轉換為了防止混亂的地圖表達(從一個鍵值表達式創建一個地圖)。
下麵的查詢轉換的值數組每個元素通過添加鍵值:
選擇鍵,值,變換(值,價值- - - - - ->價值+鍵)transformed_values從nested_data
存在(數組
、功能
):布爾
返回true,如果謂詞函數
適用於任何元素輸入數組
。
下麵的示例檢查數組包含一個元素的值模10 = 1:
選擇鍵,值,存在(值,價值- - - - - ->價值%10==1)filtered_values從nested_data
過濾器(數組
、功能
):數組
生成一個輸出數組
從輸入數組
隻有隻有謂詞添加元素函數
成立。
下麵的例子過濾器與一個值隻數組元素的值> 50允許:
選擇鍵,值,過濾器(值,價值- - - - - ->價值>50)filtered_values從nested_data
總(數組
B函數、功能R):
減少的元素數組
成一個單一的值R
通過合並元素到一個緩衝區B
使用函數
並通過應用finish函數
在最後一個緩衝區。初始值B
是由一個零的表達式。完成的功能是可選的,如果你不指定函數確定函數恒等函數(id - > id)
使用。
這是唯一的高階函數,取兩個lambda函數。
下麵的例子總結(骨料)數組的值到一個(總和)值。兩個版本完成函數(summed_values
)和一個沒有完成的功能summed_values_simple
顯示:
選擇鍵,值,減少(值,0,(價值,acc)- - - - - ->價值+acc, acc- - - - - ->acc) summed_values,減少(值,0,(價值,acc)- - - - - ->價值+acc) summed_values_simple從nested_data
你也可以計算更複雜的聚合。下麵的代碼顯示了計算幾何平均數的數組元素。
選擇鍵,值,聚合(值,(1.0作為產品,0作為N),(緩衝區,價值)- - - - - ->(價值*緩衝區。產品,buffer.N+1),緩衝- - - - - ->權力(buffer.product1.0/buffer.N) geomean從nested_data
結論
高階函數將可用磚3.0運行時。如果你有任何嵌套的數據,一定要試一試!