業務場景
teacher表中的tech_class欄位儲存的是每個老師所教授的課程,課程之間以英文逗號分隔。現在要用語句統計每個課程對應的教師數量。語句及效果如下:
語句其實很簡單,各種部落格或者gpt都有不錯且可行的解決方案,我們主要來理解下這段語句的執行原理,更好的學習。
part1 REGEXP_SUBSTR
關於REGEXP_SUBSTR的官方文件
具體語法這裡不再贅敘,我們從單個例子入手看看效果:
REGEXP_SUBSTR可以將欄位字串根據所給正規表示式匹配並拆分(注意不是分割,但效果上等同於分割)。
最後一個引數代表要取出第幾個匹配的結果:
那為什麼這裡要使用LEVEL?LEVEL是什麼?
關於LEVEL的官方解釋 具體如圖:
使用之前要注意,官方文件裡有句話:
To define a hierarchical relationship in a query, you must use the CONNECT BY clause.
所以關於connect by,你可以先往後看。
使用LEVEL後的效果:
LEVEL是一個在CONNECT BY子句中使用的偽列,它代表當前遞迴層次的級別。在每次遞迴呼叫中,LEVEL的值會增加1。在這個例子中,LEVEL的值會從1開始,一直到tech_class中逗號分隔的子串的數量——3。
為什麼這樣會有81條?我們的預期結果其實是3條。讓我們繼續探究......
Football是欄位裡的第一個值,只有1條;Basketball是欄位裡的第二個值,有10條;PingPang是欄位裡的第三個值,有70條!貌似越往後資料重複越多,而且次數增長的可怕,但很難發現出有什麼規律。檢索後基本確定出現重複資料是因為在遞迴過程中,regexp_substr函式沒有正確移動到下一個匹配項,而是重複移動到了Basketball或者PingPang,至於它底層是什麼重複移動的,額我也沒搞明白....。
對此我們需要新增prior確保每次遞迴時都能正確提取。
part3 prior
關於prior的簡單介紹
connect by中加prior可以限定父子的對應關係,限定遞迴路徑。這裡對同條記錄進行遞迴:
加sys_guid()是為了保證層次查詢,存在迴圈時,不出現無限遞迴。它為每行生成一個唯一標識,從而避免無限迴圈。
Part3 connect by
CONNECT BY的官方文件--分級查詢
connect by常常結合prior一起實現父級查詢。因此connect by LEVEL prior一般都一起出現。
附
最後再次附上針對原始的業務需求的完整的語句及輸出:
select
regexp_substr(tech_class, '[^,]+', 1, LEVEL) as class_name,
tech_name
from teacher
CONNECT BY LEVEL <= REGEXP_COUNT(tech_class, '[^,]+')
and prior tech_class = tech_class
and prior sys_guid() is not null
order by class_name