Repository navigation
Expand file tree
/
Copy pathmgmt_p_get_max_compression_order.prc
More file actions
244 lines (217 loc) · 10.8 KB
/
Copy pathmgmt_p_get_max_compression_order.prc
File metadata and controls
244 lines (217 loc) · 10.8 KB
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
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
244
REM *************************************************************************************************
REM *
REM * File: mgmt_p_get_max_compression_order.prc
REM *
REM * Author: Jeff Moss
REM *
REM * Purpose: Provides facility to get the compressability of each column in a given table
REM * Can be run at Table or Partition level and for a defined sample size.
REM * Can specify string that must be the prefix order by columns - routine will work out
REM * compressability of all the other columns individually with this prefix, e.g.
REM * TABLE X(col1, col2, col3, col4, col5, col6)
REM * specifying 'col3','col2','col5' as the prefix strings will make the routine work out compression
REM * for col1 with order by col3,col2,col5,col1
REM * ,col4 with order by col3,col2,col5,col4
REM * and col6 with order by col3,col2,col5,col6
REM *
REM * Execution:
REM * To just get the compressability of each individual column...
REM * exec mgmt_p_get_max_compress_order(USER,'BIG_TABLE','PARTITION1',10000);
REM * Then to get the compressability of the remaining columns when ordered by
REM * a commonly used access path:
REM * exec mgmt_p_get_max_compress_order(USER,'BIG_TABLE','PARTITION1',10,'COL1','COL2','COL3');
REM *
REM * History:
REM *
REM * Date Author Change
REM * 02-NOV-2005 Jeff Moss Initial version
REM * 26-NOV-2005 Jeff Moss Added facility to specify the prefix order by columns as suggested by
REM * David Aldridge.
REM * 25-JAN-2006 Jeff Moss Removed hard coded 32K block size dependency!
REM * 12-AUG-2007 Jeff Moss Removed hard coded schema name of AE_MGMT!
REM * 18-JUL-2012 Changed C_TABLES to look in USER schema for the TEMP_COL tables.
REM *************************************************************************************************
CREATE OR REPLACE
PROCEDURE mgmt_p_get_max_compress_order(p_table_owner IN VARCHAR2 DEFAULT USER
,p_table_name IN VARCHAR2
,p_partition_name IN VARCHAR2 DEFAULT NULL
,p_sample_size IN NUMBER DEFAULT 1000000
,p_prefix_column1 IN VARCHAR2 DEFAULT NULL
,p_prefix_column2 IN VARCHAR2 DEFAULT NULL
,p_prefix_column3 IN VARCHAR2 DEFAULT NULL
) IS
CURSOR c_tab_columns(b_table_name VARCHAR2
,b_table_owner VARCHAR2
,b_prefix_column1 VARCHAR2
,b_prefix_column2 VARCHAR2
,b_prefix_column3 VARCHAR2) IS
SELECT column_name
, column_id
FROM dba_tab_columns
WHERE table_name = b_table_name
AND owner = b_table_owner
AND column_name != NVL(b_prefix_column1,'1234567890123456789012345678901')
AND column_name != NVL(b_prefix_column2,'1234567890123456789012345678901')
AND column_name != NVL(b_prefix_column3,'1234567890123456789012345678901')
ORDER BY column_id;
CURSOR c_tables(b_unique_id VARCHAR2,b_table_name VARCHAR2,b_table_owner VARCHAR2) IS
SELECT dt.table_name
, dt.blocks
, dt.num_rows
, dtc.column_name
FROM dba_tables dt
, dba_tab_columns dtc
WHERE dt.table_name LIKE 'TEMP_COL%'||b_unique_id
AND dtc.table_name = b_table_name
AND dtc.owner = b_table_owner
AND dt.owner = USER --b_table_owner
AND TO_NUMBER(SUBSTR(dt.table_name,10,3)) = dtc.column_id
ORDER BY dt.blocks,dt.num_rows;
CURSOR c_get_block_size IS
SELECT (value / 1024) block_size_k
FROM v$parameter
WHERE name = 'db_block_size';
l_block_size NUMBER;
l_unique_id NUMBER;
l_master_table VARCHAR2(30);
l_sql VARCHAR2(32767);
l_column_table VARCHAR2(30);
l_order_by_prefix VARCHAR2(254);
e_942 EXCEPTION;
PRAGMA EXCEPTION_INIT(e_942,-942);
e_invalid_prefix_column EXCEPTION;
FUNCTION mgmt_f_validate_prefix_column(p_table_owner IN VARCHAR2
,p_table_name IN VARCHAR2
,p_prefix_column IN VARCHAR2) RETURN BOOLEAN IS
CURSOR c_column(b_table_owner VARCHAR2
,b_table_name VARCHAR2
,b_prefix_name VARCHAR2) IS
SELECT 1
FROM dba_tab_columns
WHERE owner = b_table_owner
AND table_name = b_table_name
AND column_name = b_prefix_name;
BEGIN
FOR r_column IN c_column(p_table_owner
,p_table_name
,p_prefix_column) LOOP
RETURN TRUE; -- we found a match
END LOOP;
RETURN FALSE; -- nothing found so no match
END mgmt_f_validate_prefix_column;
BEGIN
-- output paramters...
dbms_output.put_line(LPAD('-',100,'-'));
dbms_output.put_line('Running mgmt_p_get_max_compress_order...');
dbms_output.put_line(LPAD('-',100,'-'));
dbms_output.put_line('Table : '||p_table_name);
dbms_output.put_line('Sample Size : '||TO_CHAR(p_sample_size));
-- get a unique id to use for the working tables
l_unique_id := TO_CHAR(SYSDATE,'DDMMYYYYHH24MISS');
dbms_output.put_line('Unique Run ID: '||TO_CHAR(l_unique_id));
-- now get block size from
OPEN c_get_block_size;
FETCH c_get_block_size INTO l_block_size;
IF c_get_block_size%NOTFOUND OR c_get_block_size%NOTFOUND IS NULL THEN
CLOSE c_get_block_size;
RAISE_APPLICATION_ERROR(-20001,'Cannot get block size!');
ELSE
CLOSE c_get_block_size;
END IF;
-- Now if we have any p_prefix_columns specified then we need to factor those in...
-- Lets check the prefix_columns first to see if they are valid...
IF p_prefix_column1 IS NOT NULL THEN
IF NOT mgmt_f_validate_prefix_column(p_table_owner,p_table_name,p_prefix_column1) THEN
RAISE e_invalid_prefix_column;
END IF;
END IF;
IF p_prefix_column2 IS NOT NULL THEN
IF NOT mgmt_f_validate_prefix_column(p_table_owner,p_table_name,p_prefix_column2) THEN
RAISE e_invalid_prefix_column;
END IF;
END IF;
IF p_prefix_column3 IS NOT NULL THEN
IF NOT mgmt_f_validate_prefix_column(p_table_owner,p_table_name,p_prefix_column3) THEN
RAISE e_invalid_prefix_column;
END IF;
END IF;
-- Ok, we now have valid prefix columns...
-- let's create the prefix string for the order by clause
l_order_by_prefix := (CASE WHEN p_prefix_column1 IS NOT NULL THEN p_prefix_column1||',' END)||
(CASE WHEN p_prefix_column2 IS NOT NULL THEN p_prefix_column2||',' END)||
(CASE WHEN p_prefix_column3 IS NOT NULL THEN p_prefix_column3||',' END);
dbms_output.put_line('ORDER BY Prefix: '||SUBSTR(l_order_by_prefix,1,LENGTH(l_order_by_prefix)-1));
dbms_output.put_line(LPAD('-',100,'-'));
l_master_table := 'TEMP_MASTER_'||TO_CHAR(l_unique_id);
l_sql := 'CREATE TABLE '||l_master_table||
' NOLOGGING COMPRESS AS SELECT * FROM '||p_table_owner||'.'||p_table_name;
IF p_partition_name IS NOT NULL THEN
l_sql := l_sql||' PARTITION('||p_partition_name||')';
END IF;
l_sql := l_sql||' WHERE ROWNUM <= '||TO_CHAR(p_sample_size);
dbms_output.put_line('Creating MASTER Table : '||l_master_table);
execute immediate(l_sql);
FOR r_tab_columns IN c_tab_columns(p_table_name,p_table_owner,p_prefix_column1,p_prefix_column2,p_prefix_column3) LOOP
dbms_output.put_line('Creating COLUMN Table '||TO_CHAR(r_tab_columns.column_id)||': '||r_tab_columns.column_name);
l_column_table := 'TEMP_COL_'||LPAD(TO_CHAR(r_tab_columns.column_id),3,'0')||'_'||TO_CHAR(l_unique_id);
l_sql := 'CREATE TABLE '||l_column_table||
' NOLOGGING COMPRESS AS SELECT * FROM '||l_master_table||
' ORDER BY '||l_order_by_prefix||r_tab_columns.column_name;
execute immediate(l_sql);
DBMS_STATS.GATHER_TABLE_STATS (ownname=>USER,tabname=>l_column_table,estimate_percent=>100);
END LOOP;
-- Now print out individual column compressions...
dbms_output.put_line(LPAD('-',100,'-'));
dbms_output.put_line('The output below lists each column in the table and the number of blocks/rows and space');
dbms_output.put_line(' used when the table data is ordered by only that column, or in the case where a prefix');
dbms_output.put_line(' has been specified, where the table data is ordered by the prefix and then that column.');
dbms_output.put_line(' ');
dbms_output.put_line('From this one can determine if there is a specific ORDER BY which can be applied to');
dbms_output.put_line(' to the data in order to maximise compression within the table whilst, in the case of a');
dbms_output.put_line(' a prefix being present, ordering data as efficiently as possible for the most common ');
dbms_output.put_line(' access path(s).');
dbms_output.put_line(LPAD('-',100,'-'));
dbms_output.put_line('NAME COLUMN BLOCKS ROWS SPACE_GB');
dbms_output.put_line('============================== ============================== ============ ============ ========');
FOR r_tables IN c_tables(l_unique_id,p_table_name,p_table_owner) LOOP
dbms_output.put_line(RPAD(r_tables.table_name,31)||
RPAD(r_tables.column_name,30)||
LPAD(TO_CHAR(r_tables.blocks),13)||
LPAD(TO_CHAR(r_tables.num_rows),13)||
LPAD(TO_CHAR(ROUND(r_tables.blocks * l_block_size / (1024*1024),4)),9)
);
END LOOP;
dbms_output.put_line(LPAD('-',100,'-'));
-- First drop the Master table...
l_sql := 'DROP TABLE '||l_master_table;
BEGIN
execute immediate(l_sql);
EXCEPTION
WHEN e_942 THEN NULL;
END;
-- Next drop the Column tables...
FOR r_tab_columns IN c_tab_columns(p_table_name,p_table_owner,p_prefix_column1,p_prefix_column2,p_prefix_column3) LOOP
l_sql := 'DROP TABLE TEMP_COL_'||LPAD(TO_CHAR(r_tab_columns.column_id),3,'0')||'_'||TO_CHAR(l_unique_id);
BEGIN
execute immediate(l_sql);
EXCEPTION
WHEN e_942 THEN NULL;
END;
END LOOP;
EXCEPTION
WHEN e_invalid_prefix_column THEN
RAISE_APPLICATION_ERROR(-20002,'mgmt_p_get_max_compress_order: Invalid prefix column specified.');
WHEN OTHERS THEN
-- Clear up the schema
-- First drop the Master table...
l_sql := 'DROP TABLE '||l_master_table;
execute immediate(l_sql);
-- Next drop the Column tables...
FOR r_tab_columns IN c_tab_columns(p_table_name,p_table_owner,p_prefix_column1,p_prefix_column2,p_prefix_column3) LOOP
l_sql := 'DROP TABLE TEMP_COL_'||LPAD(TO_CHAR(r_tab_columns.column_id),3,'0')||'_'||TO_CHAR(l_unique_id);
execute immediate(l_sql);
END LOOP;
-- Now raise an application error...
RAISE_APPLICATION_ERROR(-20001,'mgmt_p_get_max_compress_order encountered error: '||SQLERRM);
END mgmt_p_get_max_compress_order;
/