Trouble with Package Invalidating Itself:
585069Feb 13 2008 — edited Feb 13 2008Hello All,
First, please let me know if this is not the place for this.
Second, I'm trying to create a generic pivot table package such that developers can call it from ColdFusion. I've implemented it by having the package generate and execute DDL (using EXECUTE IMMEDIATE) but the problem is the tables that are being created are dependencies of the package causing it to generate this error each time it is run.
ORA-04068: existing state of packages has been discarded ORA-04061: existing state of package body "HRPAYROLL.ORA_TOOLS" has been invalidated ORA-06508: PL/SQL: could not find program unit being called ORA-06512: at line 1
It runs fine after automatic recompilation (refreshing the web page) but this won't work so well for users. Any suggestions would be greatly appreciated. I've attached the code for the package and the procedure call from CF.
Package:
CREATE OR REPLACE PACKAGE BODY ORA_TOOLS is
procedure PIVOT (
p_MAIN_DESC IN varchar2,
p_PIV_FIELD IN varchar2,
p_AGG_FUNC IN varchar2,
p_AGG_FIELD IN varchar2,
r_RESULT_SET IN OUT RESULT_SET
) is
v_PIV_FIELD varchar2(50):= p_PIV_FIELD;
v_AGG_FUNC varchar2(50):= p_AGG_FUNC;
v_AGG_FIELD varchar2(50):= p_AGG_FIELD;
v_PIV_FIELD_DT varchar2(50);
v_PIV_FIELD_SZ varchar2(50);
v_DATE date;
v_MAIN_DATA varchar2(2000):= 'create table main_data as ( ';/*Will equal p_MAIN_DESC*/
v_TABLE_NAME varchar2(50);
-- usage exec ora_tools.pivot('1','em_sex','COUNT','em_employee_id')
/* Create distinct list of values to use as piv column headings */
v_DIST_VALUES varchar2(2000):=
'create table dist_values as
select distinct nvl('||v_PIV_FIELD||',''UNKNOWN'') as piv_fields from MAIN_DATA';
/* Create pivot/aggregate tables */
v_CREATE_PIVOT varchar2(4000);
v_CREATE_AGG varchar2(4000);
v_GROUP_BY varchar2(4000);
/* Create Pivot_Table insert text */
v_INSERT_FIELDS varchar2(4000);
v_INSERT_TEXT varchar2(4000);
v_PIVOT_INSERT varchar2(250);
v_INSERT_FIELDS_COUNT number;
v_PIV_COL_NUM number;
v_COUNTER number := 1;
/* Drop temporary tables */
v_DROP_MAIN_DATA varchar2(50):='drop table MAIN_DATA';
v_DROP_DIST_VALUES varchar2(50):='drop table DIST_VALUES';
v_DROP_PIV_TABLE varchar2(50):='drop table PIV_TABLE';
v_DROP_AGG_TABLE varchar2(50):='drop table AGG_TABLE';
/* Create cursors */
cursor c_MAIN_DATA_FIELDS is
select COLUMN_NAME,
DATA_TYPE,
DATA_LENGTH,
DATA_PRECISION
from user_tab_columns
where TABLE_NAME='MAIN_DATA' and
upper(COLUMN_NAME) != upper(v_PIV_FIELD) and
upper(COLUMN_NAME) != upper(v_AGG_FIELD);
v_MAIN_DATA_FIELDS c_MAIN_DATA_FIELDS%rowtype;
cursor c_PIVOT_FIELDS is
select nvl(PIV_FIELDS,'UNKNOWN') as PIV_FIELDS from DIST_VALUES;
v_PIVOT_FIELDS c_PIVOT_FIELDS%rowtype;
cursor c_TRANSFORM_DATA is
select * from AGG_TABLE;
v_TRANSFORM_DATA c_TRANSFORM_DATA%rowtype;
cursor c_GET_TABLE_NAME is
select table_name from user_tables where table_name = v_TABLE_NAME;
v_GET_TABLE_NAME c_GET_TABLE_NAME%rowtype;
begin
/* Creates the MAIN_DATA sql */
v_MAIN_DATA := v_MAIN_DATA ||p_MAIN_DESC;
v_MAIN_DATA := v_MAIN_DATA||')';
/*Parse the statements to drop the temp tables*/
execute immediate v_DROP_MAIN_DATA;
execute immediate v_DROP_DIST_VALUES;
execute immediate v_DROP_PIV_TABLE;
execute immediate v_DROP_AGG_TABLE;
/*Parse the statement to create the temp tables*/
execute immediate v_MAIN_DATA;
execute immediate v_DIST_VALUES;
/* sets the datatype for the aggregate field in the piv_table */
if upper(v_AGG_FUNC) in ('SUM','AVG','COUNT') then
v_PIV_FIELD_DT := 'NUMBER';
v_PIV_FIELD_SZ := '10,5';
else
select data_type, data_length into v_PIV_FIELD_DT, v_PIV_FIELD_SZ
from user_tab_columns
where table_name='DIST_VALUES';
end if;
/* Add Main Data Elements */
open c_MAIN_DATA_FIELDS;
v_CREATE_PIVOT := 'create table PIV_TABLE ('; /* Create Pivot Table */
v_CREATE_AGG := 'create table AGG_TABLE as select '; /* Create Agg Table */
v_INSERT_FIELDS_COUNT := 0;
fetch c_MAIN_DATA_FIELDS into v_MAIN_DATA_FIELDS;
while c_MAIN_DATA_FIELDS%found loop
v_CREATE_PIVOT := v_CREATE_PIVOT||' '||v_MAIN_DATA_FIELDS.column_name||' '||v_MAIN_DATA_FIELDS.data_type||' ('||v_MAIN_DATA_FIELDS.data_length||')';
v_CREATE_AGG := v_CREATE_AGG||' '||v_MAIN_DATA_FIELDS.column_name;
v_GROUP_BY := v_GROUP_BY||' '||v_MAIN_DATA_FIELDS.column_name;
v_INSERT_FIELDS := v_INSERT_FIELDS||' '||v_MAIN_DATA_FIELDS.column_name; /* Used to create the pivot insert record */
fetch c_MAIN_DATA_FIELDS into v_MAIN_DATA_FIELDS;
v_CREATE_PIVOT := v_CREATE_PIVOT||',';
v_CREATE_AGG := v_CREATE_AGG||', ';
v_GROUP_BY := v_GROUP_BY||', ';
v_INSERT_FIELDS := v_INSERT_FIELDS||', ';
v_INSERT_FIELDS_COUNT := v_INSERT_FIELDS_COUNT + 1;
end loop;
close c_MAIN_DATA_FIELDS;
/* Add Pivot Data Elements */
open c_PIVOT_FIELDS;
fetch c_PIVOT_FIELDS into v_PIVOT_FIELDS;
v_CREATE_PIVOT := v_CREATE_PIVOT||' "'||nvl(v_PIVOT_FIELDS.piv_fields,'UNKNOWN')||'" '||v_PIV_FIELD_DT||' ('||v_PIV_FIELD_SZ||')';
fetch c_PIVOT_FIELDS into v_PIVOT_FIELDS;
while c_PIVOT_FIELDS%found loop
v_CREATE_PIVOT := v_CREATE_PIVOT||',"'||nvl(v_PIVOT_FIELDS.piv_fields,'UNKNOWN')||'" '||v_PIV_FIELD_DT||' ('||v_PIV_FIELD_SZ||')';
fetch c_PIVOT_FIELDS into v_PIVOT_FIELDS;
end loop;
close c_PIVOT_FIELDS;
/* End the Create Table */
v_CREATE_PIVOT := v_CREATE_PIVOT||' )';
/* End Create Pivot Table */
/* debugging code */
/*
insert into debug_stuff (stuff) values (v_CREATE_PIVOT);
commit;
*/
execute immediate v_CREATE_PIVOT;
/* Aggregate functions COUNT, SUM, AVG, MAX, MIN */
v_CREATE_AGG := v_CREATE_AGG||v_PIV_FIELD||',';
If upper(v_AGG_FUNC) = 'COUNT' then
v_CREATE_AGG := v_CREATE_AGG||' COUNT('||v_AGG_FIELD||') as AGG_DATA FROM MAIN_DATA GROUP BY '||v_GROUP_BY;
elsif upper(v_AGG_FUNC) = 'SUM' then
v_CREATE_AGG := v_CREATE_AGG||' SUM(to_number('||v_AGG_FIELD||')) as AGG_DATA FROM MAIN_DATA GROUP BY '||v_GROUP_BY;
elsif upper(v_AGG_FUNC) = 'AVG' then
v_CREATE_AGG := v_CREATE_AGG||' AVG(to_number('||v_AGG_FIELD||')) as AGG_DATA FROM MAIN_DATA GROUP BY '||v_GROUP_BY;
elsif upper(v_AGG_FUNC) = 'MAX' then
v_CREATE_AGG := v_CREATE_AGG||' MAX('||v_AGG_FIELD||') as AGG_DATA FROM MAIN_DATA GROUP BY '||v_GROUP_BY;
elsif upper(v_AGG_FUNC) = 'MIN' then
v_CREATE_AGG := v_CREATE_AGG||' MIN('||v_AGG_FIELD||') as AGG_DATA FROM MAIN_DATA GROUP BY '||v_GROUP_BY;
end if;
v_CREATE_AGG := v_CREATE_AGG||v_PIV_FIELD; /* Adds the pivot field to the group by list */
--insert into debug_stuff (stuff) values (v_CREATE_AGG); commit;
/* Generates the table containing aggregate data */
execute immediate v_CREATE_AGG;
/*Example of the query that is constructed below
select em_payroll_unit, pud_payroll_unit_name,
max( decode( em_employee_status,'TR', AGG_DATA, null )),
max( decode( em_employee_status,'AC', AGG_DATA, null )),
max( decode( em_employee_status,'LV', AGG_DATA, null ))
from ( select *
from agg_table
)
group by em_payroll_unit, pud_payroll_unit_name;
*/
/*Construct the Pivot Query*/
v_INSERT_TEXT := 'insert into piv_table select ';
open c_MAIN_DATA_FIELDS;
fetch c_MAIN_DATA_FIELDS into v_MAIN_DATA_FIELDS;
v_INSERT_TEXT := v_INSERT_TEXT||' '||v_MAIN_DATA_FIELDS.COLUMN_NAME;
fetch c_MAIN_DATA_FIELDS into v_MAIN_DATA_FIELDS;
while c_MAIN_DATA_FIELDS%FOUND loop
v_INSERT_TEXT := v_INSERT_TEXT||', '||v_MAIN_DATA_FIELDS.COLUMN_NAME;
fetch c_MAIN_DATA_FIELDS into v_MAIN_DATA_FIELDS;
end loop;
close c_MAIN_DATA_FIELDS;
open c_PIVOT_FIELDS;
fetch c_PIVOT_FIELDS into v_PIVOT_FIELDS;
while c_PIVOT_FIELDS%FOUND loop
v_INSERT_TEXT := v_INSERT_TEXT||', max(decode(nvl('||v_PIV_FIELD||',''UNKNOWN''), '''||nvl(v_PIVOT_FIELDS.PIV_FIELDS,'UNKNOWN')||''', AGG_DATA, null)) ';
fetch c_PIVOT_FIELDS into v_PIVOT_FIELDS;
end loop;
close c_PIVOT_FIELDS;
v_INSERT_TEXT := v_INSERT_TEXT||' from (select * from agg_table) group by ';
open c_MAIN_DATA_FIELDS;
fetch c_MAIN_DATA_FIELDS into v_MAIN_DATA_FIELDS;
v_INSERT_TEXT := v_INSERT_TEXT||' '||v_MAIN_DATA_FIELDS.COLUMN_NAME;
fetch c_MAIN_DATA_FIELDS into v_MAIN_DATA_FIELDS;
while c_MAIN_DATA_FIELDS%FOUND loop
v_INSERT_TEXT := v_INSERT_TEXT||', '||v_MAIN_DATA_FIELDS.COLUMN_NAME;
fetch c_MAIN_DATA_FIELDS into v_MAIN_DATA_FIELDS;
end loop;
close c_MAIN_DATA_FIELDS;
/* debugging code */
/*insert into debug_stuff (stuff) values (v_INSERT_TEXT);
commit;
*/
execute immediate v_INSERT_TEXT;
commit;
OPEN r_RESULT_SET for SELECT * FROM PIV_TABLE;
--RETURN;
/*------------- Exception Handling ------------------------*/
exception
WHEN OTHERS THEN
v_ErrorNumber := SQLCODE;
v_ErrorText := SUBSTR(SQLERRM, 1, 200);
insert into errors
values (sysdate,
v_ErrorNumber||' , '||v_ErrorText||' ORA_TOOLS');
commit;
v_TABLE_NAME := 'MAIN_DATA';
open c_GET_TABLE_NAME;
fetch c_GET_TABLE_NAME into v_GET_TABLE_NAME;
if c_GET_TABLE_NAME%NOTFOUND then
execute immediate 'create table MAIN_DATA (field1 varchar2(50))';
end if;
close c_GET_TABLE_NAME;
v_TABLE_NAME := 'DIST_VALUES';
open c_GET_TABLE_NAME;
fetch c_GET_TABLE_NAME into v_GET_TABLE_NAME;
if c_GET_TABLE_NAME%NOTFOUND then
execute immediate 'create table DIST_VALUES (PIV_FIELDS varchar2(50))';
end if;
close c_GET_TABLE_NAME;
v_TABLE_NAME := 'AGG_TABLE';
open c_GET_TABLE_NAME;
fetch c_GET_TABLE_NAME into v_GET_TABLE_NAME;
if c_GET_TABLE_NAME%NOTFOUND then
execute immediate 'create table AGG_TABLE (field1 varchar2(50))';
end if;
close c_GET_TABLE_NAME;
v_TABLE_NAME := 'PIV_TABLE';
open c_GET_TABLE_NAME;
fetch c_GET_TABLE_NAME into v_GET_TABLE_NAME;
if c_GET_TABLE_NAME%NOTFOUND then
execute immediate 'create table PIV_TABLE (field1 varchar2(50))';
end if;
close c_GET_TABLE_NAME;
end PIVOT;
END ORA_TOOLS;
CF Call code:
<cfstoredproc procedure="ora_tools.pivot" datasource="hrpayroll">
<!--- The query the generates the raw data set --->
<cfprocparam
value = "select emf.em_payroll_group,
emf.em_payroll_unit,
pud.pud_payroll_unit_name as pud_name,
emf.em_employee_id
from emf, pud
where emf.em_payroll_unit = pud.pud_payroll_unit
and em_employee_status = 'AC'"
cfsqltype = "cf_sql_varchar">
<!--- The field that you want to pivot on --->
<cfprocparam
value = "em_payroll_group"
cfsqltype = "cf_sql_varchar">
<!--- The aggregate function you want included can be one of COUNT, SUM, AVG, MAX, MIN --->
<cfprocparam
value = "COUNT"
cfsqltype = "cf_sql_varchar">
<!--- Field that you want to perform the aggregate function upon --->
<cfprocparam
value = "em_employee_id"
cfsqltype = "cf_sql_varchar">
<cfprocresult name="result">
</cfstoredproc>
<cfdump var="#result#">
Thank you for your help,
Mike