EXCEL的透视表是非常棒的一个功能,对于竖表转横表比较有用,我个人认为是除了那些公式计算以外EXCEL最吸引人的功能了。但是EXCEL有着65535行的限制,对于我们这些经常要为经营分析取数、做报表的人显然是远远不够的。Oracle中当然可以实现表的pivot,在Expert One on One Oracle中讲分析函数的一章中也有一个例子讲解如何Pivot,但是Tom的pivot是类似于完全的转换,将几列全部打散(具体请到asktom.oracle.com搜索“pivot”或者参考上述书籍)。而我们需要的是一个Oracle的透视表。也就是如下的效果:
1
CREATE OR REPLACE PACKAGE pkg_pivot
2
AS
3
/******************************************************************************
4
NAME: pkg_pivot
5
PURPOSE:
6
7
REVISIONS:
8
Ver Date Author Description
9
--------- ---------- --------------- ------------------------------------
10
1.0 2005-12-21 ERN 1. 创建包
11
2.0 2005-12-22 ERN 2. 增加pivot_long过程,处理超过200列
12
情况,但仍存在限制
13
******************************************************************************/
14
TYPE refcursor IS REF CURSOR;
15
TYPE ARRAY IS TABLE OF VARCHAR2(30) index by binary_integer;
16
PROCEDURE pivot(p_tablename varchar2,
17
p_anchor varchar2,
18
p_pivot varchar2,
19
p_value varchar2,
20
p_cursor OUT refcursor);
21
PROCEDURE pivot_long(p_tablename varchar2,
22
p_anchor varchar2,
23
p_pivot varchar2,
24
p_value varchar2);
25
END;
26
/
27
create or replace package body pkg_pivot as
28
procedure pivot(p_tablename varchar2, --表名,也可以输入查询
29
p_anchor varchar2, --不变的列名,对于多个列可以用逗号分隔
30
p_pivot varchar2, --将取值转换成列的列名
31
p_value varchar2, --填充的值字段
32
p_cursor out refcursor --返回结果集
33
) as
34
/******************************************************************************
35
NAME: pivot
36
PURPOSE: 竖表转横表,此过程返回结果集,仅处理组合后32767字节以下的语句。
37
即大约可以处理200列的转换。
38
39
REVISIONS:
40
Ver Date Author Description
41
--------- ---------- --------------- ------------------------------------
42
1.0 2005-12-21 ERN 1. 创建
43
44
******************************************************************************/
45
ar_col array; --存放转换后的列名
46
n_cnt number;
47
l_query varchar2(32766); --最终的执行语句
48
l_query_pivot varchar2(200); --统计p_pivot转换后的列数与取值
49
begin
50
n_cnt := 0;
51
l_query := 'select ' || p_anchor || ',';
52
l_query_pivot := 'select distinct ' || p_pivot || ' from ' ||
53
p_tablename;
54
open p_cursor for l_query_pivot;
55
56
loop
57
exit when p_cursor%NOTFOUND;
58
n_cnt := n_cnt + 1;
59
fetch p_cursor
60
into ar_col(n_cnt);
61
end loop;
62
n_cnt := n_cnt - 1;
63
close p_cursor;
64
65
for i in 1 .. n_cnt - 1 loop
66
l_query := l_query || 'max(val' || to_char(i) || ') "' || ar_col(i) || '",';
67
end loop;
68
69
l_query := l_query || 'max(val' || to_char(n_cnt) || ') "' ||
70
ar_col(n_cnt) || '" ';
71
l_query := l_query || 'from (select ';
72
73
l_query := l_query || p_anchor || ', ';
74
75
for i in 1 .. n_cnt - 1 loop
76
l_query := l_query || 'decode(' || p_pivot || ',' || ar_col(i) ||
77
',rn, null) rn' || to_char(i) || ',';
78
l_query := l_query || 'decode(' || p_pivot || ',' || ar_col(i) || ',' ||
79
p_value || ',null) val' || to_char(i) || ',';
80
81
end loop;
82
l_query := l_query || 'decode(' || p_pivot || ',' || ar_col(n_cnt) ||
83
', rn, null) rn' || to_char(n_cnt) || ',';
84
l_query := l_query || 'decode(' || p_pivot || ',' || ar_col(n_cnt) || ',' ||
85
p_value || ',null) val' || to_char(n_cnt) || ' ';
86
87
l_query := l_query || 'from (select ';
88
89
l_query := l_query || p_anchor || ', ';
90
91
l_query := l_query || p_pivot || ', ' || p_value ||
92
', row_number() over(partition by ';
93
94
l_query := l_query || p_anchor || ', ';
95
96
l_query := l_query || p_pivot || ' ';
97
l_query := l_query || 'order by ' || p_value || ') rn from ' ||
98
p_tablename || ') t) t group by ';
99
100
l_query := l_query || p_anchor || ' ';
101
102
execute immediate 'alter session set cursor_sharing=force';
103
104
open p_cursor for l_query;
105
106
execute immediate 'alter session set cursor_sharing=exact';
107
108
end;
109
procedure pivot_long(p_tablename varchar2,
110
p_anchor varchar2,
111
p_pivot varchar2,
112
p_value varchar2) as
113
/******************************************************************************
114
NAME: pivot_long
115
PURPOSE: 竖表转横表,此过程建立_ext表,用于处理组合后查询语句超过32676
116
字节的情况;对超长语句必须用dbms_sql进行解析、执行。但此处还受
117
聚集过程中限制,对于过长的分组聚集语句会报ORA-01467,内部机制是
118
受块大小影响,除非使用16K的大块,否则无法绕过此问题。
119
120
REVISIONS:
121
Ver Date Author Description
122
--------- ---------- --------------- ------------------------------------
123
1.0 2005-12-22 ERN 1. 创建
124
125
******************************************************************************/
126
ar_col array;
127
n_cnt number;
128
l_tmp varchar2(3256);
129
p_cursor refcursor;
130
l_query dbms_sql.varchar2s;
131
n_ind number;
132
n_left number;
133
l_query_pivot varchar2(3200);
134
l_cursor integer default dbms_sql.open_cursor;
135
n_result number;
136
begin
137
-- l_query(1) :='1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890';
138
n_cnt := 0;
139
140
l_query(1) := 'create table ' ||
141
substr(p_tablename,
142
instr(p_tablename, '.') + 1,
143
instr(p_tablename, 'where') -
144
instr(p_tablename, '.') - 2) || 'ext as ';
145
146
n_ind := 2;
147
l_query(n_ind) := 'select ' || p_anchor || ',';
148
l_query_pivot := 'select distinct ' || p_pivot || ' from ' ||
149
p_tablename;
150
open p_cursor for l_query_pivot;
151
152
loop
153
exit when p_cursor%NOTFOUND;
154
n_cnt := n_cnt + 1;
155
fetch p_cursor
156
into ar_col(n_cnt);
157
end loop;
158
n_cnt := n_cnt - 1;
159
close p_cursor;
160
161
162
n_ind := n_ind + 1;
163
l_query(n_ind) := '';
164
for i in 1 .. n_cnt - 1 loop
165
l_tmp := 'max(val' || to_char(i) || ') "' || ar_col(i) || '",';
166
-- if NVL(length(l_query(n_ind)), 0) + length(l_tmp) <= 255 then
167
if NVL(length(l_query(n_ind)), 0) + length(l_tmp) <= 100 then
168
-- gl dbms_output.put_line(l_query(n_ind));
169
-- gl dbms_output.put_line(lengthb(l_query(n_ind)));
170
l_query(n_ind) := l_query(n_ind) || l_tmp;
171
else
172
n_left := 100 - length(l_query(n_ind));
173
l_query(n_ind) := l_query(n_ind) || substr(l_tmp, 1, n_left);
174
n_ind := n_ind + 1;
175
l_query(n_ind) := substr(l_tmp, n_left + 1);
176
end if;
177
end loop;
178
n_ind := n_ind + 1;
179
l_query(n_ind) := '';
180
181
l_query(n_ind) := 'max(val' || to_char(n_cnt) || ') "' || ar_col(n_cnt) || '" ';
182
l_query(n_ind) := l_query(n_ind) || 'from (select ';
183
184
l_query(n_ind) := l_query(n_ind) || p_anchor || ', ';
185
n_ind := n_ind + 1;
186
l_query(n_ind) := '';
187
for i in 1 .. n_cnt - 1 loop
188
l_tmp := 'decode(' || p_pivot || ',''' || ar_col(i) || ''',rn, null) rn' ||
189
to_char(i) || ',';
190
l_tmp := l_tmp || 'decode(' || p_pivot || ',''' || ar_col(i) || ''',' ||
191
p_value || ',null) val' || to_char(i) || ',';
192
if NVL(length(l_query(n_ind)), 0) + length(l_tmp) <= 100 then
193
l_query(n_ind) := l_query(n_ind) || l_tmp;
194
else
195
n_left := 100 - length(l_query(n_ind));
196
l_query(n_ind) := l_query(n_ind) || substr(l_tmp, 1, n_left);
197
n_ind := n_ind + 1;
198
l_query(n_ind) := substr(l_tmp, n_left + 1);
199
end if;
200
201
end loop;
202
n_ind := n_ind + 1;
203
l_query(n_ind) := '';
204
l_query(n_ind) := 'decode(' || p_pivot || ',''' || ar_col(n_cnt) ||
205
''', rn, null) rn' || to_char(n_cnt) || ',';
206
l_query(n_ind) := l_query(n_ind) || 'decode(' || p_pivot || ',''' ||
207
ar_col(n_cnt) || ''',' || p_value || ',null) val' ||
208
to_char(n_cnt) || ' ';
209
n_ind := n_ind + 1;
210
l_query(n_ind) := '';
211
l_query(n_ind) := 'from (select ';
212
213
l_query(n_ind) := l_query(n_ind) || p_anchor || ', ';
214
215
l_query(n_ind) := l_query(n_ind) || p_pivot || ', ' || p_value ||
216
', row_number() over(partition by ';
217
218
l_query(n_ind) := l_query(n_ind) || p_anchor || ', ';
219
220
l_query(n_ind) := l_query(n_ind) || p_pivot || ' ';
221
n_ind := n_ind + 1;
222
l_query(n_ind) := '';
223
l_query(n_ind) := l_query(n_ind) || 'order by ' || p_value ||
224
') rn from ' || p_tablename || ') t) t group by ';
225
226
l_query(n_ind) := l_query(n_ind) || p_anchor || ' ';
227
228
-- for i in 1..n_ind loop
229
-- dbms_output.put_line(l_query(i));
230
-- end loop;
231
232
dbms_sql.parse(c => l_cursor,
233
statement => l_query,
234
lb => l_query.first,
235
ub => l_query.last,
236
lfflg => false,
237
language_flag => 1);
238
239
n_result := dbms_sql.execute(c => l_cursor);
240
dbms_sql.close_cursor(c => l_cursor);
241
end;
242
end pkg_pivot;
243
/

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

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

此项的引用通告 URL 是:
http://yaoyp.spaces.live.com/blog/cns!ac5fd97b8a549660!534.trak
引用此项的日志