Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
-- ----------------------------------------------------------------------------------------------
--
-- 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;