淺析REGEXP_SUBSTR,PRIOR,CONNECT BY

摸鱼小天才發表於2024-11-26

業務場景

image

teacher表中的tech_class欄位儲存的是每個老師所教授的課程,課程之間以英文逗號分隔。現在要用語句統計每個課程對應的教師數量。語句及效果如下:

image

語句其實很簡單,各種部落格或者gpt都有不錯且可行的解決方案,我們主要來理解下這段語句的執行原理,更好的學習。

part1 REGEXP_SUBSTR

關於REGEXP_SUBSTR的官方文件

具體語法這裡不再贅敘,我們從單個例子入手看看效果:

image

image

REGEXP_SUBSTR可以將欄位字串根據所給正規表示式匹配並拆分(注意不是分割,但效果上等同於分割)。

最後一個引數代表要取出第幾個匹配的結果:

image

那為什麼這裡要使用LEVEL?LEVEL是什麼?

關於LEVEL的官方解釋 具體如圖:

image

使用之前要注意,官方文件裡有句話:

To define a hierarchical relationship in a query, you must use the CONNECT BY clause.

所以關於connect by,你可以先往後看。

使用LEVEL後的效果:

image

LEVEL是一個在CONNECT BY子句中使用的偽列,它代表當前遞迴層次的級別。在每次遞迴呼叫中,LEVEL的值會增加1。在這個例子中,LEVEL的值會從1開始,一直到tech_class中逗號分隔的子串的數量——3。

為什麼這樣會有81條?我們的預期結果其實是3條。讓我們繼續探究......

image

image

image

Football是欄位裡的第一個值,只有1條;Basketball是欄位裡的第二個值,有10條;PingPang是欄位裡的第三個值,有70條!貌似越往後資料重複越多,而且次數增長的可怕,但很難發現出有什麼規律。檢索後基本確定出現重複資料是因為在遞迴過程中,regexp_substr函式沒有正確移動到下一個匹配項,而是重複移動到了Basketball或者PingPang,至於它底層是什麼重複移動的,額我也沒搞明白....。

對此我們需要新增prior確保每次遞迴時都能正確提取。

part3 prior

關於prior的簡單介紹
connect by中加prior可以限定父子的對應關係,限定遞迴路徑。這裡對同條記錄進行遞迴:

image

加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

image

相關文章