-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathccard.sql
175 lines (163 loc) · 7.27 KB
/
ccard.sql
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
-- ----------------------------------------------------------------------------------------------
--
-- Script: ccard.sql
--
-- Utility: CCARD (Collection CARDinality)
--
-- Author: Adrian Billington
-- www.oracle-developer.net
--
-- Description: A simple utility named CCARD to provide the CBO with the exact cardinality of a
-- collection used in a TABLE() query. Without it, the CBO uses a default value
-- (8,168 rows on a database with an 8kb block size) and this is usually inaccurate
-- and can lead to sub-optimal execution plans. The effect of CCARD is similar to
-- that of the CARDINALITY hint, except that CCARD uses supported methods and the
-- CARDINALITY hint is undocumented (up to 11.1.0.7 at the time of writing).
--
-- The CCARD utility is implemented by a function and an Oracle Data Cartridge for
-- the Extensible Optimiser. Once installed, the CCARD utility is simple to use (it
-- is just a function call wrapped around a collection in a TABLE query). See the
-- Usage section below for examples. For more information on the mechanism itself,
-- read http://www.oracle-developer.net/display.php?id=427.
--
-- The most common use of small collections in SQL is for variable in-lists, with
-- queries of the form:
--
-- SELECT {columns}
-- FROM table_name
-- WHERE column_name IN (SELECT column_value
-- FROM TABLE(collection));
--
-- CCARD is primarily designed for use with these types of variable in-list
-- queries but it can also be used for queries of the form:
--
-- SELECT {columns}
-- FROM TABLE(collection);
--
-- See the Usage section for specific examples.
--
-- CCARD cannot be used where the collection is derived from another table
-- function (see Restrictions below).
--
-- Usage: a) Variable in-list in SQL with a hard-coded collection
-- -------------------------------------------------------
--
-- SELECT {columns}
-- FROM table_name
-- WHERE column_name IN (SELECT column_value
-- FROM TABLE(
-- ccard(
-- ccard_ntt('A','B','C'))));
--
-- b) Variable in-list in PL/SQL with collection parameter
-- -------------------------------------------------------
--
-- PROCEDURE procedure_name( p_inlist IN ccard_ntt ) IS
-- ...
-- CURSOR cur IS
-- SELECT {columns}
-- FROM table_name
-- WHERE column_name IN (SELECT column_value
-- FROM TABLE(ccard(p_inlist)));
-- ...
-- BEGIN
-- ...
-- END procedure_name;
--
-- Benefits: Without CCARD:
--
-- SELECT *
-- FROM TABLE(collection_type('A','B','C'));
--
-- --------------------------------------------------------------
-- | Id | Operation | Name | Rows |
-- --------------------------------------------------------------
-- | 0 | SELECT STATEMENT | | 8168 |
-- | 1 | COLLECTION ITERATOR CONSTRUCTOR FETCH| | |
-- --------------------------------------------------------------
--
-- With CCARD:
--
-- SELECT *
-- FROM TABLE(ccard(collection_type('A','B','C')));
--
-- -----------------------------------------------------------
-- | Id | Operation | Name | Rows |
-- -----------------------------------------------------------
-- | 0 | SELECT STATEMENT | | 3 |
-- | 1 | COLLECTION ITERATOR PICKLER FETCH| CCARD | |
-- -----------------------------------------------------------
--
-- Restrictions: CCARD cannot be used when the collection is supplied by another table function.
-- For example, for a query such as SELECT * FROM TABLE(ccard(table_function)),
-- the CBO will default to an incorrect cardinality of 1 (rounded up from 0). This
-- means that variable in-list queries of the following forms will not benefit from
-- the CCARD utility:
--
-- SELECT {columns}
-- FROM TABLE(ccard(string_to_table(:some_delimited_string)));
--
-- SELECT {columns}
-- FROM TABLE(ccard(string_to_table('A,B,C')));
--
-- In such cases, the advice is to use best practice and pass collections as
-- collections and not as delimited strings that need to be parsed!
--
-- Versions: This utility will work for all versions of 10g and upwards.
--
-- Required: 1) CREATE PROCEDURE
-- 2) CREATE TYPE
-- 3) CREATE PUBLIC SYNONYM (see bottom of script and exclude as necessary)
--
-- Disclaimer: http://www.oracle-developer.net/disclaimer.php
--
-- ----------------------------------------------------------------------------------------------
CREATE TYPE ccard_ntt AS TABLE OF VARCHAR2(4000);
/
CREATE FUNCTION ccard (
p_collection IN ccard_ntt
) RETURN ccard_ntt DETERMINISTIC AS
BEGIN
RETURN p_collection;
END ccard;
/
CREATE TYPE ccard_ot AS OBJECT (
dummy_attribute NUMBER,
STATIC FUNCTION ODCIGetInterfaces (
p_interfaces OUT SYS.ODCIObjectList
) RETURN NUMBER,
STATIC FUNCTION ODCIStatsTableFunction (
p_function IN SYS.ODCIFuncInfo,
p_stats OUT SYS.ODCITabFuncStats,
p_args IN SYS.ODCIArgDescList,
p_collection IN ccard_ntt
) RETURN NUMBER
);
/
CREATE TYPE BODY ccard_ot AS
STATIC FUNCTION ODCIGetInterfaces (
p_interfaces OUT SYS.ODCIObjectList
) RETURN NUMBER IS
BEGIN
p_interfaces := SYS.ODCIObjectList(
SYS.ODCIObject ('SYS', 'ODCISTATS2')
);
RETURN ODCIConst.success;
END ODCIGetInterfaces;
STATIC FUNCTION ODCIStatsTableFunction (
p_function IN SYS.ODCIFuncInfo,
p_stats OUT SYS.ODCITabFuncStats,
p_args IN SYS.ODCIArgDescList,
p_collection IN ccard_ntt
) RETURN NUMBER IS
BEGIN
p_stats := SYS.ODCITabFuncStats(p_collection.COUNT);
RETURN ODCIConst.success;
END ODCIStatsTableFunction;
END;
/
ASSOCIATE STATISTICS WITH FUNCTIONS ccard USING ccard_ot;
CREATE PUBLIC SYNONYM ccard_ntt FOR ccard_ntt;
CREATE PUBLIC SYNONYM ccard FOR ccard;
GRANT EXECUTE ON ccard_ntt TO PUBLIC;
GRANT EXECUTE ON ccard TO PUBLIC;