oracle中,wm_concat函數是一個聚合函數,和mysql中的group_concat函數類似,不過group_concat函數比較強大,可以定義分隔符和排序,當然所謂強大是相對的,這里假使我們不知道oracle中的over函數,也不知道listagg函數。
我們先來看看wm_concat函數能實現什么功能,通俗點==>列傳行,如果不明白,請看下面截圖(可以看到分隔符默認為','順序也是雜亂的)
所以,接下來,我們開始重寫wm_concat函數(需要注意和需要說明的地方放在代碼注釋中...)
(1) 因為需要進行排序,首先自定義一個可變數組
1
2
|
-- 定義可變數組,字符串類型,長度32767,存放列值
CREATE
OR
REPLACE
TYPE WYARRAY
as
TABLE
OF
VARCHAR
(32767) ;
|
(2)自定義排序函數、分隔符函數
1
2
3
4
5
6
7
8
9
10
11
12
|
-- 定義分隔符函數
create
or
replace
function
delimiter(colValue
in
varchar2,
delimiter
in
varchar2)
return
varchar2
is
rtnValue varchar2(32767);
begin
rtnValue := colValue ||
' delimiter=>'
|| delimiter ||
'; '
;
return
rtnValue;
end
delimiter;
|
1
2
3
4
5
6
7
8
9
10
11
12
|
-- 定義排序函數
create
or
replace
function
orderby(colValue
in
varchar2,
orderby
in
varchar2)
return
varchar2
is
rtnValue varchar2(32767);
begin
rtnValue := colValue ||
' orderby=>'
||
LOWER
(orderby) ||
'; '
;
return
rtnValue;
end
orderby;
|
(3) 重定義oracle接口函數、以及接口函數的實現體(實現分隔符和排序的主要代碼)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
-- 使用當前用戶權限(使用authid current_user,定義type為用戶當前用戶的權限,舉個例子:比如A用戶他可以建立表,但是A用戶在存儲過程中如果建立表可能會提示權限不夠,所以需要用authid current_user進行約束)
create
or
replace
type wy_wm_concat authid
current_user
as
object
(
--拼接字符串,存放中間值,當然也可以定義為clob,clob會使用臨時段,導致臨時表空間迅速增大;
--查看wmsys下的function可以發現Oracle10g到oracle11g自帶的wm_concat函數的返回類型從clob變成varchar2
currStr VARCHAR2(32767),
--分割字符串
delimiter VARCHAR2(64),
--排序字符串(asc、desc)
orderby VARCHAR2(64),
-- 定義字符串數組
strArray WYARRAY,
-- 初始化接口函數
STATIC
FUNCTION
ODCIAGGREGATEINITIALIZE(init
IN
OUT
wy_wm_concat)
RETURN
NUMBER,
-- 迭代接口函數
MEMBER
FUNCTION
ODCIAGGREGATEITERATE(self
IN
OUT
wy_wm_concat,
colValue
IN
VARCHAR2)
RETURN
NUMBER,
-- 並行時字符串合並的接口函數
MEMBER
FUNCTION
ODCIAGGREGATEMERGE(self
IN
OUT
wy_wm_concat,
next
wy_wm_concat)
RETURN
NUMBER,
-- oracle終止接口函數
MEMBER
FUNCTION
ODCIAGGREGATETERMINATE(self
IN
wy_wm_concat,
returnValue
OUT
VARCHAR2,
flags
IN
NUMBER)
RETURN
NUMBER
)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
create
or
replace
type body wy_wm_concat
is
--定義函數的body
--初始化函數
STATIC
FUNCTION
ODCIAGGREGATEINITIALIZE(init
IN
OUT
wy_wm_concat)
RETURN
NUMBER
is
begin
init := wy_wm_concat(
''
,
','
,
'asc'
, WYARRAY());
return
ODCICONST.Success;
END
;
-- 字符串拼接,self 為當前聚集函數的指針,用來與前面的計算結果進行關聯
MEMBER
FUNCTION
ODCIAGGREGATEITERATE(self
IN
OUT
wy_wm_concat,
colValue
IN
VARCHAR2)
RETURN
NUMBER
is
tempStr
varchar
(500);
extendStr
varchar
(500);
deStr
varchar
(100);
deLen
int
default
0;
segStr
varchar
(500);
--定義一個二維數組
TYPE varArry
IS
VARRAY(2)
OF
VARCHAR2(200);
tempArry varArry := varArry(
''
,
''
);
begin
if instr(colValue,
' '
, 1) > 0
then
tempStr := substr(colValue, 1, instr(colValue,
' '
, 1) - 1);
else
tempStr := colValue;
end
if;
--排序和分隔符
extendStr :=
REPLACE
(colValue, tempStr ||
' '
);
if instr(extendStr,
' '
, 1) > 0
then
tempArry(1) := substr(extendStr, 1, instr(extendStr,
' '
, 1) - 1);
tempArry(2) := substr(extendStr, instr(extendStr,
' '
, 1));
for
i
in
1 .. tempArry.
count
loop
-- 獲取分隔符
if (tempArry(i)
is
not
null
)
and
(instr(tempArry(i),
'delimiter=>'
) > 0)
THEN
deStr :=
'delimiter=>'
;
deLen := length(deStr);
segStr := substr(trim(tempArry(i)),
instr(trim(tempArry(i)), deStr) + deLen);
self.delimiter := SUBSTR(segStr, 1, instr(segStr,
';'
, -1) - 1);
END
IF;
-- 獲取排序字符串
if tempArry(i)
is
not
null
and
(instr(tempArry(i),
'orderby=>'
) > 0)
THEN
deStr :=
'orderby=>'
;
deLen := length(deStr);
segStr := substr(trim(tempArry(i)),
instr(trim(tempArry(i)), deStr) + deLen);
self.orderby := SUBSTR(segStr, 1, instr(segStr,
';'
, -1) - 1);
END
IF;
end
loop;
end
if;
-- 存放入數組
self.strArray.extend;
self.strArray(self.strArray.
count
) := tempStr;
return
ODCICONST.Success;
END
;
--並行操作是用來合並兩個聚集函數的兩個不同的指針對應的結果
MEMBER
FUNCTION
ODCIAGGREGATEMERGE(self
IN
OUT
wy_wm_concat,
next
wy_wm_concat)
RETURN
NUMBER
is
begin
-- 將next數組中元素全部放入self指針對應的數組中
for
i
in
1 ..
next
.strArray.
count
loop
self.strArray.extend;
self.strArray(self.strArray.
count
) :=
next
.strArray(i);
end
loop;
return
ODCICONST.Success;
END
;
-- 終止函數,返回結果
MEMBER
FUNCTION
ODCIAGGREGATETERMINATE(self
IN
wy_wm_concat,
returnValue
OUT
VARCHAR2,
flags
IN
NUMBER)
RETURN
NUMBER
IS
temp_rtnValue varchar2(32767);
BEGIN
-- 排序
if INSTR(self.orderby,
'desc'
) > 0
THEN
for
x
in
(
select
column_value
from
Table
(self.strArray)
order
by
1
DESC
) loop
temp_rtnValue := temp_rtnValue || self.delimiter || x.column_value;
end
loop;
ELSE
for
x
in
(
select
column_value
from
Table
(self.strArray)
order
by
1
ASC
) loop
temp_rtnValue := temp_rtnValue || self.delimiter || x.column_value;
end
loop;
END
IF;
returnValue := ltrim(temp_rtnValue, self.delimiter);
return
ODCICONST.Success;
END
;
END
;
|
(4)自定義聚集函數
1
2
3
|
-- 定義聚集函數(未開啟並行計算功能)
create
or
replace
function
wy_concat(colValue VARCHAR2)
RETURN
VARCHAR2
AGGREGATE USING wy_wm_concat;
|
至此,主要的代碼已經全部奉上,看看運行效果,如下截圖:
①看看調用的默認情況(分隔符默認是逗號,排序默認是升序,在初始化函數中如此定義的)
②自定義分隔符(利用分隔符函數將分隔符定義為*)
③降序排序
④去重,為了可以使用wm_concat自帶的去重函數,所以在自定義分隔符和排序函數時,實質是實用了字符串處理(如果你覺得處理字符串麻煩,可以自定義 type... as object ,在使用的時候可以很方便,不會用的童鞋可以私下問)