Permalink
Cannot retrieve contributors at this time
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?
utilities/csort.sql
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
204 lines (175 sloc)
7.56 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- ---------------------------------------------------------------------------------------------- | |
-- | |
-- Script: csort.sql | |
-- | |
-- Utility: CSORT (Collection SORTer) | |
-- | |
-- Author: Adrian Billington | |
-- www.oracle-developer.net | |
-- | |
-- Description: A simple package containing two functions to sort collections: | |
-- | |
-- 1) sort | |
-- 2) sort_small | |
-- | |
-- These functions contain techniques suitable for the size of collection that | |
-- needs to be sorted. In most cases, the SORT function will be the most efficient | |
-- to use. The SORT_SMALL function should be the most efficient when dealing with | |
-- tiny collections of just a few small elements. For more information on sorting | |
-- collections and choosing the right function, read the following article: | |
-- | |
-- http://www.oracle-developer.net/display.php?id=428 | |
-- | |
-- This package supports collections of a single nested table type of strings. | |
-- Nested table types are the most flexible of collections - they can be used | |
-- in all SQL and PL/SQL assignments and they are supported by all MULTISET | |
-- operations. To implement sorts for your own standard collection type, simply | |
-- replace the name of the type used throughout the utility. | |
-- | |
-- This utility can be made generic (i.e. to support any nested table type) by | |
-- using either ANYDATA or subtitutable types, but this would be slower and more | |
-- complex to use. | |
-- | |
-- Usage: a) Sort a pre-populated collection in SQL | |
-- ----------------------------------------- | |
-- SELECT CSORT.SORT(collection) | |
-- FROM table_name; | |
-- | |
-- b) Sort a pre-populated small collection in PL/SQL | |
-- -------------------------------------------------- | |
-- PROCEDURE ... ( p_collection IN varchar2_ntt ) IS | |
-- v_sorted_collection varchar2_ntt := varchar2_ntt(); | |
-- BEGIN | |
-- ... | |
-- v_sorted_collection := CSORT.SORT_SMALL(p_collection); | |
-- ... | |
-- END; | |
-- | |
-- c) Sort a collection descending in SQL | |
-- -------------------------------------- | |
-- SELECT CSORT.SORT(collection, 'Y') | |
-- FROM table_name; | |
-- | |
-- d) Distinct sort a collection in SQL | |
-- ------------------------------------ | |
-- SELECT CSORT.SORT(collection, 'N', 'Y') | |
-- FROM table_name; | |
-- | |
-- ...or... | |
-- | |
-- SELECT CSORT.SORT(collection, NULL, 'Y') | |
-- FROM table_name; | |
-- | |
-- e) Distinct descending sort in SQL | |
-- ---------------------------------- | |
-- SELECT CSORT.SORT(collection, 'Y', 'Y') | |
-- FROM table_name; | |
-- | |
-- Versions: This utility will work for all versions of 9i Release 2 and upwards. | |
-- To make it 8i compatible, remove the SORT_SMALL function and use the | |
-- SORT function for all requirements. | |
-- | |
-- 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 varchar2_ntt AS TABLE OF VARCHAR2(4000); | |
/ | |
CREATE PACKAGE csort AS | |
FUNCTION sort( p_collection IN varchar2_ntt, | |
p_descending IN VARCHAR2 DEFAULT 'N', | |
p_distinct IN VARCHAR2 DEFAULT 'N' ) | |
RETURN varchar2_ntt; | |
FUNCTION sort_small( p_collection IN varchar2_ntt, | |
p_descending IN VARCHAR2 DEFAULT 'N', | |
p_distinct IN VARCHAR2 DEFAULT 'N' ) | |
RETURN varchar2_ntt; | |
END csort; | |
/ | |
CREATE PACKAGE BODY csort AS | |
FUNCTION boolean_option( p_option IN VARCHAR2 ) | |
RETURN BOOLEAN IS | |
BEGIN | |
RETURN UPPER(p_option) = 'Y'; | |
END boolean_option; | |
----------------------------------------------------------------- | |
FUNCTION sort( | |
p_collection IN varchar2_ntt, | |
p_descending IN VARCHAR2 DEFAULT 'N', | |
p_distinct IN VARCHAR2 DEFAULT 'N' ) RETURN varchar2_ntt IS | |
v_collection varchar2_ntt := varchar2_ntt(); | |
BEGIN | |
EXECUTE IMMEDIATE | |
'SELECT ' || CASE | |
WHEN boolean_option(p_distinct) | |
THEN 'DISTINCT' | |
END || ' column_value | |
FROM TABLE(:p_collection) | |
ORDER BY column_value ' || CASE | |
WHEN boolean_option(p_descending) | |
THEN 'DESC' | |
ELSE 'ASC' | |
END | |
BULK COLLECT INTO v_collection | |
USING p_collection; | |
RETURN v_collection; | |
END sort; | |
----------------------------------------------------------------- | |
FUNCTION sort_small( p_collection IN varchar2_ntt, | |
p_descending IN VARCHAR2 DEFAULT 'N', | |
p_distinct IN VARCHAR2 DEFAULT 'N' ) | |
RETURN varchar2_ntt IS | |
TYPE sorter_aat IS TABLE OF PLS_INTEGER | |
INDEX BY VARCHAR2(4000); | |
v_collection varchar2_ntt := varchar2_ntt(); | |
v_sorter sorter_aat; | |
v_sorter_idx VARCHAR2(4000); | |
v_source_idx PLS_INTEGER; | |
v_descending BOOLEAN := boolean_option(p_descending); | |
v_distinct BOOLEAN := boolean_option(p_distinct); | |
BEGIN | |
-- Sort the collection using the sorter array... | |
-- -------------------------------------------------- | |
v_source_idx := p_collection.FIRST; | |
WHILE v_source_idx IS NOT NULL LOOP | |
v_sorter_idx := p_collection(v_source_idx); | |
v_sorter(v_sorter_idx) := CASE | |
WHEN NOT v_sorter.EXISTS(v_sorter_idx) | |
OR v_distinct | |
THEN 1 | |
ELSE v_sorter(v_sorter_idx) + 1 | |
END; | |
v_source_idx := p_collection.NEXT(v_source_idx); | |
END LOOP; | |
-- Assign sorted elements back to collection... | |
-- -------------------------------------------------- | |
v_sorter_idx := CASE | |
WHEN v_descending | |
THEN v_sorter.LAST | |
ELSE v_sorter.FIRST | |
END; | |
WHILE v_sorter_idx IS NOT NULL LOOP | |
-- Handle multiple copies of same value. For distinct | |
-- collections, there will only be one element... | |
-- -------------------------------------------------- | |
FOR i IN 1 .. v_sorter(v_sorter_idx) LOOP | |
v_collection.EXTEND; | |
v_collection(v_collection.LAST) := v_sorter_idx; | |
END LOOP; | |
v_sorter_idx := CASE | |
WHEN v_descending | |
THEN v_sorter.PRIOR(v_sorter_idx) | |
ELSE v_sorter.NEXT(v_sorter_idx) | |
END; | |
END LOOP; | |
RETURN v_collection; | |
END sort_small; | |
END csort; | |
/ | |
CREATE PUBLIC SYNONYM varchar2_ntt FOR varchar2_ntt; | |
CREATE PUBLIC SYNONYM csort FOR csort; | |
GRANT EXECUTE ON varchar2_ntt TO PUBLIC; | |
GRANT EXECUTE ON csort TO PUBLIC; |