重寫Oracle的wm_concat函數,自定義分隔符、排序


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  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  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  in  ( select  column_value
                   from  Table (self.strArray)
                  order  by  DESC ) loop
       
         temp_rtnValue := temp_rtnValue || self.delimiter || x.column_value;
       
       end  loop;
     ELSE
       for  in  ( select  column_value  from  Table (self.strArray)  order  by  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 ,在使用的時候可以很方便,不會用的童鞋可以私下問)

   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM