Invalid Datatype with user defined function - sql

I created this package and function to return a sequence of dates, given a start date and stop date (optional).
The package compiles and I can see that it is there with no errors in TOAD. Anytime I try to execute the function, I receive an error stating:
[Error] Execution (10: 8): ORA-00902: invalid datatype
I do have the right privileges as Ive created other packages with procs and functions; therefore, I'm sure its due to problems with my code.
The logic is below:
CREATE OR REPLACE PACKAGE TEST_PKG AS
TYPE table_of_dates IS TABLE OF date;
FUNCTION get_date_table(p_begin in date
,p_stop in date default sysdate
,p_next in integer default 1)
RETURN table_of_dates;
END TEST_PKG;
/
CREATE OR REPLACE package body TEST_PKG as
function get_date_table(p_begin in date
,p_stop in date default sysdate
,p_next in integer default 1)
RETURN table_of_dates
IS
date_table table_of_dates;
BEGIN
with date_sq as (
select
p_begin begin_date
,p_stop end_date from dual
)
select
d.begin_date + level - p_next
bulk collect into date_table
from date_sq d
where
1=1
/* TODO: Check this math */
connect by level <= (d.end_date - d.begin_date) + p_step
;
RETURN date_table;
END get_date_table;
end TEST_PKG;
/
Some example code to try to test the function is:
select TEST_PKG.get_date_table(sysdate) from dual; -- <--causes error shown above

If you want to use that type at SQL level (you do), then you have to create it at SQL level, not within the package.
SQL> create or replace type table_of_dates is table of date;
2 /
Type created.
Package spec:
SQL> CREATE OR REPLACE PACKAGE TEST_PKG AS
2
3
4 -- TYPE table_of_dates IS TABLE OF date;
5
6 FUNCTION get_date_table(p_begin in date
7 ,p_stop in date default sysdate
8 ,p_next in integer default 1)
9 RETURN table_of_dates;
10
11 END TEST_PKG;
12
13 /
Package created.
Package body: what is p_step at line #25? You never declared it, so I commented it.
SQL> CREATE OR REPLACE package body TEST_PKG as
2
3 function get_date_table(p_begin in date
4 ,p_stop in date default sysdate
5 ,p_next in integer default 1)
6 RETURN table_of_dates
7 IS
8
9 date_table table_of_dates;
10
11 BEGIN
12
13 with date_sq as (
14 select
15 p_begin begin_date
16 ,p_stop end_date from dual
17 )
18 select
19 d.begin_date + level - p_next
20 bulk collect into date_table
21 from date_sq d
22 where
23 1=1
24 /* TODO: Check this math */
25 connect by level <= (d.end_date - d.begin_date) -- + p_step
26 ;
27
28
29 RETURN date_table;
30 END get_date_table;
31
32 end TEST_PKG;
33 /
Package body created.
Testing:
SQL> select * From table(test_pkg.get_date_table(date '2021-07-15'));
COLUMN_VAL
----------
15.07.2021
16.07.2021
17.07.2021
18.07.2021
19.07.2021
SQL>
Seems to be OK (as far as the error you reported is concerned).

Related

IF condition containing IN operator inside ORACLE Trigger is not working

I have a trigger that uses IF condition with IN operator and two variables v_audit_user and v_evdnse_user inside IN. Both variables are containing comma separated ID values. The trigger gets compiled successfully with no errors. I am not understanding why the IF condition with IN is not working. When I select the function that assigns value to the variables independently, I do see the comma separated values, so nothing is wrong with function (see screenshot).
create or replace TRIGGER TRG_CHK_HRCQA_CASE_ACTIONS
AFTER INSERT ON KDD_CASE_ACTIONS
FOR EACH ROW
DECLARE
user_audit kdd_review_owner.OWNER_ID%TYPE; /* The user that is displayed in audit */
user_evdnse kdd_review_owner.OWNER_ID%TYPE; /* The user that took action in evidence tab */
v_audit_user NUMBER; /* The HRCO/QA user from audit tab */
v_evdnse_user NUMBER; /* The HRCO/QA user from evidence tab */
LV_ERRORCODE VARCHAR2(1000);
BEGIN
/* pass the username into the variables */
SELECT kro.OWNER_ID into user_audit from kdd_review_owner kro where kro.OWNER_SEQ_ID = :NEW.ACTION_BY_ID;
SELECT kro.OWNER_ID into user_evdnse from kdd_review_owner kro where kro.OWNER_SEQ_ID = :NEW.ACTION_BY_ID;
/* fetch the comma separated IDs */
v_audit_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID,user_audit,'AUDIT');
v_evdnse_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID,user_evdnse,'EVDNSE');
-- select ENTITY_ID into v_evdnse_user from table(f_get_arg_table(F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID,user_evdnse,'EVDNSE')));
/* If the action taken is by QA or HRCO role */
IF (:NEW.ACTION_SEQ_ID in (v_audit_user,v_evdnse_user))
THEN
/* then insert record in the SC_HRCQA_CASE_ACTIONS table with IS_HRCO_QA flag as Y */
Insert into SC_HRCQA_CASE_ACTIONS (ACTION_SEQ_ID,ACTION_BY_ID,ACTION_TS,STATUS_CD,CASE_INTRL_ID,ACTION_ID,NEW_CASE_OWNR_ASSGN_ID,CASE_DUE_TS,PREV_CASE_OWNR_ASSGN_ID,IS_HRCO_QA)
values (:NEW.ACTION_SEQ_ID, :NEW.ACTION_BY_ID, :NEW.ACTION_TS, :NEW.STATUS_CD, :NEW.CASE_INTRL_ID, :NEW.ACTION_ID, :NEW.NEW_CASE_OWNR_ASSGN_ID, :NEW.CASE_DUE_TS, :NEW.PREV_CASE_OWNR_ASSGN_ID,'Y');
-- ELSE
--
-- /* else the logged in user is NOT HRCO/QA hence insert record in the SC_HRCQA_CASE_ACTIONS table with IS_HRCO_QA flag as N */
--
-- Insert into SC_HRCQA_CASE_ACTIONS (ACTION_SEQ_ID,ACTION_BY_ID,ACTION_TS,STATUS_CD,CASE_INTRL_ID,ACTION_ID,NEW_CASE_OWNR_ASSGN_ID,CASE_DUE_TS,PREV_CASE_OWNR_ASSGN_ID,IS_HRCO_QA)
-- values (:NEW.ACTION_SEQ_ID, :NEW.ACTION_BY_ID, :NEW.ACTION_TS, :NEW.STATUS_CD, :NEW.CASE_INTRL_ID, :NEW.ACTION_ID, :NEW.NEW_CASE_OWNR_ASSGN_ID, :NEW.CASE_DUE_TS, :NEW.PREV_CASE_OWNR_ASSGN_ID,'N');
END IF;
EXCEPTION
WHEN OTHERS THEN LV_ERRORCODE := SQLCODE;
INSERT INTO KDD_LOGS_MSGS (LOG_DT, LOG_INFO_TX, REMARK_TX)
VALUES (SYSDATE,'ErrorCode - ' || LV_ERRORCODE,'TRG_CHK_HRCQA_CASE_ACTIONS');
END;
You cannot pass a comma-delimited string stored in a single variable to an IN condition and expect it to be parsed as multiple values as it is not.
If you want to use a single variable containing a delimited list then you will need to use string functions to find a sub-string match:
create or replace TRIGGER TRG_CHK_HRCQA_CASE_ACTIONS
AFTER INSERT ON KDD_CASE_ACTIONS
FOR EACH ROW
DECLARE
v_owner_id kdd_review_owner.OWNER_ID%TYPE;
v_audit_user VARCHAR2(1000);
v_evdnse_user VARCHAR2(1000);
LV_ERRORCODE VARCHAR2(1000);
BEGIN
SELECT OWNER_ID
into v_owner_id -- You only need one variable here
from kdd_review_owner
where OWNER_SEQ_ID = :NEW.ACTION_BY_ID;
v_audit_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID, v_owner_id, 'AUDIT');
v_evdnse_user := F_GET_HRCQA_ACTIONS(:NEW.CASE_INTRL_ID, v_owner_id, 'EVDNSE');
IF ','||v_audit_user||','||v_evdnse_user||',' LIKE '%,'||:NEW.ACTION_SEQ_ID||',%'
THEN
Insert into SC_HRCQA_CASE_ACTIONS (
ACTION_SEQ_ID, ACTION_BY_ID, ACTION_TS, STATUS_CD, CASE_INTRL_ID,
ACTION_ID, NEW_CASE_OWNR_ASSGN_ID, CASE_DUE_TS, PREV_CASE_OWNR_ASSGN_ID, IS_HRCO_QA
) values (
:NEW.ACTION_SEQ_ID, :NEW.ACTION_BY_ID, :NEW.ACTION_TS, :NEW.STATUS_CD, :NEW.CASE_INTRL_ID,
:NEW.ACTION_ID, :NEW.NEW_CASE_OWNR_ASSGN_ID, :NEW.CASE_DUE_TS, :NEW.PREV_CASE_OWNR_ASSGN_ID,'Y'
);
END IF;
EXCEPTION
WHEN OTHERS THEN LV_ERRORCODE := SQLCODE;
INSERT INTO KDD_LOGS_MSGS (LOG_DT, LOG_INFO_TX, REMARK_TX)
VALUES (SYSDATE,'ErrorCode - ' || LV_ERRORCODE,'TRG_CHK_HRCQA_CASE_ACTIONS');
END;
/
I don't have your tables so I'll try to illustrate it using my own code.
This is what you're doing now: to us (humans), it is obvious that L_NEW_ID (its value is 10) is contained in L_VAR1 (its value is '10,20'). However, Oracle doesn't recognize that and returns "Not OK":
SQL> declare
2 -- does L_NEW_ID exist in L_VAR1 and L_VAR2 (using your code)?
3 l_new_id number := 10;
4 --
5 l_var1 varchar2(20) := '10,20';
6 l_var2 varchar2(20) := '30,40';
7 begin
8 if l_new_id in (l_var1, l_var2) then
9 dbms_output.put_line('OK');
10 else
11 dbms_output.put_line('Not OK');
12 end if;
13 end;
14 /
Not OK
PL/SQL procedure successfully completed.
SQL>
So, what to do? One option is to split variables to rows and then check whether search value (10, right?) exists in such a list of values (rows). The result is - as you can see - "OK":
SQL> declare
2 l_new_id number := 10;
3 --
4 l_var1 varchar2(20) := '10,20';
5 l_var2 varchar2(20) := '30,40';
6 l_cnt number;
7 begin
8 -- count how many times L_NEW_ID exists in L_VAR1 nad L_VAR2
9 select count(*)
10 into l_cnt
11 from (-- split L_VAR1 into rows
12 select regexp_substr(l_var1, '[^,]+', 1, level) val
13 from dual
14 connect by level <= regexp_count(l_var1, ',') + 1
15 union all
16 -- split L_VAR2 into rows
17 select regexp_substr(l_var2, '[^,]+', 1, level)
18 from dual
19 connect by level <= regexp_count(l_var2, ',') + 1
20 ) x
21 where x.val = l_new_id;
22
23 if l_cnt > 0 then
24 dbms_output.put_line('OK');
25 else
26 dbms_output.put_line('Not OK');
27 end if;
28 end;
29 /
OK
PL/SQL procedure successfully completed.
SQL>
Just to verify it, let's change L_NEW_ID value to e.g. 99 and see what happens; as expected, "Not OK" as 99 isn't contained in 10, 20 nor 30, 40.
SQL> l2
2* l_new_id number := 10;
SQL> c/10/99
2* l_new_id number := 99;
SQL> /
Not OK
PL/SQL procedure successfully completed.
SQL>

check if two values are present in a table with plsql in oracle sql

I'm trying to create a procedure, that checks if two values are present in a table.
The logic is as follows: Create a function called get_authority. This function takes two parameters (found in the account_owner table): cust_id and acc_id, and returns 1 (one), if the customer has the right to make withdrawals from the account, or 0 (zero), if the customer doesn't have any authority to the account. I'm writing plsql and using oracle live sql. I can't figure out how to handle the scenario where a customer has two accounts!
account_owner is seen here:
create or replace function get_authority(
p_cust_id in account_owner.cust_id%type,
p_acc_id in account_owner.acc_id%type
)
return varchar2
as
v_return number(1);
v_acc_id account_owner.acc_id%type;
v_cust_id account_owner.cust_id%type;
begin
for v_ in (select account_owner.cust_id,
account_owner.acc_id
from account_owner
where p_cust_id = cust_id)
LOOP
if p_cust_id = v_cust_id and p_acc_id = v_acc_id then
v_return := v_return + 1;
else
v_return := v_return + 0;
end if;
return v_return;
END LOOP;
end;
/
When I check for the cust_id I get the return 0 - but it should be 1??
select get_authority('650707-1111',123) from dual;
return:
GET_AUTHORITY('650707-1111',123)
0
What do I do wrong?
You got 0? How come; should be NULL.
v_return number(1);
so it is initially NULL. Later on, you're adding "something" to it, but - adding anything to NULL will be NULL:
SQL> select 25 + null as result from dual;
RESULT
----------
SQL>
Therefore, set its default value to 0 (zero):
v_return number(1) := 0;
Also, you declared two additional variables:
v_acc_id account_owner.acc_id%type;
v_cust_id account_owner.cust_id%type;
Then you compare them to values passed as parameters; as they are NULL, ELSE is executed.
Furthermore, there's a loop, but you don't do anything with it. If you meant that this:
for v_ in (select account_owner.cust_id,
(rewritten as for v_ in (select cust_id) evaluates to v_cust_id - it does not. Cursor variables are referred to as v_.cust_id (note the dot in between).
Also, if there's only one row per p_cust_id and p_acc_id, why do you use cursor FOR loop at all? To avoid no_data_found or too_many_rows? I wouldn't do that; yes, it fixes such "errors", but is confusing. You'd rather properly handle exceptions.
Here's what you might have done:
Sample data:
SQL> select * From account_owner;
ACCOW_ID CUST_ID ACC_ID
---------- ----------- ----------
1 650707-1111 123
2 560126-1148 123
3 650707-1111 5899
Function; if there are more rows per parameters' combination, max function will make sure that too_many_rows is avoided (as it bothers you). You don't really care what it returns - important is that select returns anything to prove that authority exists for that account.
SQL> create or replace function get_authority
2 (p_cust_id in account_owner.cust_id%type,
3 p_acc_id in account_owner.acc_id%type
4 )
5 return number
6 is
7 l_accow_id account_owner.accow_id%type;
8 begin
9 select max(o.accow_id)
10 into l_accow_id
11 from account_owner o
12 where o.cust_id = p_cust_id
13 and o.acc_id = p_acc_id;
14
15 return case when l_accow_id is not null then 1
16 else 0
17 end;
18 end;
19 /
Function created.
Testing:
SQL> select get_authority('650707-1111', 123) res_1,
2 get_authority('650707-1111', 5899) res_2
3 from dual;
RES_1 RES_2
---------- ----------
1 1
SQL>

PL SQL Loop Table after insert and update

I'm trying loop through a table called broker after an insert to a different table called appointment and update a value on the broker table this what i have. When i try to create the trigger it comes up with an error saying "Trigger created with compilation errors"
CREATE OR REPLACE TRIGGER broker_level_trigger
AFTER INSERT ON appointment
DECLARE
counter integer := 1;
BEGIN
for o in (SELECT * FROM broker)
loop
SELECT COUNT(appointment.broker_id) INTO app_number FROM appointment INNER JOIN broker ON broker.broker_id = appointment.broker_id WHERE broker.broker_id = counter;
IF app_number > 15 THEN
UPDATE broker SET broker_level = 'gold' WHERE broker_id = counter;
counter := counter + 1;
end loop;
end;
/
The broker table has a field called broker_level and it changes based on the appointments, i want it to change if the broker_id field on appointments gets to more then 15
show err is a wonderful thing. (I created dummy tables you use).
SQL> CREATE OR REPLACE TRIGGER broker_level_trigger
2 AFTER INSERT ON appointment
3 DECLARE
4 counter integer := 1;
5 BEGIN
6 for o in (SELECT * FROM broker)
7 loop
8 SELECT COUNT(appointment.broker_id)
9 INTO app_number
10 FROM appointment INNER JOIN broker
11 ON broker.broker_id = appointment.broker_id
12 WHERE broker.broker_id = counter;
13
14 IF app_number > 15 THEN
15 UPDATE broker SET
16 broker_level = 'gold'
17 WHERE broker_id = counter;
18 counter := counter + 1;
19
20 end loop;
21 end;
22 /
Warning: Trigger created with compilation errors.
SQL> show err
Errors for TRIGGER BROKER_LEVEL_TRIGGER:
LINE/COL ERROR
-------- -----------------------------------------------------------------
18/7 PLS-00103: Encountered the symbol "LOOP" when expecting one of
the following:
if
If you look closer, you'll see that IF misses its END IF. Let's add it:
SQL> CREATE OR REPLACE TRIGGER broker_level_trigger
2 AFTER INSERT ON appointment
3 DECLARE
4 counter integer := 1;
5 BEGIN
6 for o in (SELECT * FROM broker)
7 loop
8 SELECT COUNT(appointment.broker_id)
9 INTO app_number
10 FROM appointment INNER JOIN broker
11 ON broker.broker_id = appointment.broker_id
12 WHERE broker.broker_id = counter;
13
14 IF app_number > 15 THEN
15 UPDATE broker SET
16 broker_level = 'gold'
17 WHERE broker_id = counter;
18 counter := counter + 1;
19 END IF; --> missing
20 end loop;
21 end;
22 /
Warning: Trigger created with compilation errors.
SQL> show err
Errors for TRIGGER BROKER_LEVEL_TRIGGER:
LINE/COL ERROR
-------- -----------------------------------------------------------------
6/5 PL/SQL: SQL Statement ignored
7/12 PLS-00201: identifier 'APP_NUMBER' must be declared
8/7 PL/SQL: ORA-00904: : invalid identifier
12/5 PL/SQL: Statement ignored
12/8 PLS-00201: identifier 'APP_NUMBER' must be declared
APP_NUMBER is missing now; you use it, but never declared it. Let's do it now:
SQL> CREATE OR REPLACE TRIGGER broker_level_trigger
2 AFTER INSERT ON appointment
3 DECLARE
4 counter integer := 1;
5 app_number number; --> missing
6 BEGIN
7 for o in (SELECT * FROM broker)
8 loop
9 SELECT COUNT(appointment.broker_id)
10 INTO app_number
11 FROM appointment INNER JOIN broker
12 ON broker.broker_id = appointment.broker_id
13 WHERE broker.broker_id = counter;
14
15 IF app_number > 15 THEN
16 UPDATE broker SET
17 broker_level = 'gold'
18 WHERE broker_id = counter;
19 counter := counter + 1;
20 END IF; --> missing
21 end loop;
22 end;
23 /
Trigger created.
SQL>
That would be it.
If you don't use SQL*Plus but some other tool, you can always query user_errors:
Warning: Trigger created with compilation errors.
SQL> select line, position, text from user_errors where name = 'BROKER_LEVEL_TRIGGER' order by sequence;
LINE POSITION TEXT
----- --------- ------------------------------------------------------------
8 12 PLS-00201: identifier 'APP_NUMBER' must be declared
9 7 PL/SQL: ORA-00904: : invalid identifier
7 5 PL/SQL: SQL Statement ignored
13 8 PLS-00201: identifier 'APP_NUMBER' must be declared
13 5 PL/SQL: Statement ignored
SQL>

Oracle Object inside object inside table

I have problem with oracle objects. I am writing a Function. which have SELECT:
CURSOR cResultValues (p_vrc_mnemo VARCHAR2,
p_pdt_mnemo VARCHAR2,
p_table t_crt_list_prdt_conf_tab) IS
SELECT pdt_grp_mnemo,
pdt_mnemo,
pdt_variant,
FROM TABLE(p_table)
WHERE pdt_mnemo = p_pdt_mnemo AND
pdt_variant = p_vrc_mnemo;
and to make more clear global types:
CREATE OR REPLACE TYPE t_pdt_config_rec IS OBJECT(
pdt_grp_mnemo VARCHAR2(30),
pdt_mnemo VARCHAR2(30),
pdt_variant VARCHAR2(30),
/
CREATE OR REPLACE TYPE t_pdt_config_tab IS TABLE OF t_pdt_config_rec
/
-------------------------------------------------------------------------------------
CREATE OR REPLACE TYPE t_list_conf_rec IS OBJECT(
pdt_conf t_pdt_config_rec, -- product info
pdt_childs t_pdt_config_tab) -- products compinations
/
CREATE OR REPLACE TYPE t_list_conf_tab IS TABLE OF t_list_conf_rec
/
And so on. Before changes there was only t_pdt_config_tab and I have no problems.
How could I reach information inside pdt_conf object?
It is not clear what your problem is. I can only guess that you changed the type of the procedure parameter from t_pdt_config_rec to a more complex object type t_list_conf_rec and now you can't access the values in it.
Let's build a similar example:
SQL> CREATE OR REPLACE TYPE t_pdt_config_rec IS OBJECT(
2 pdt_mnemo VARCHAR2(30),
3 pdt_variant VARCHAR2(30))
4 /
Type created
SQL> CREATE OR REPLACE TYPE t_pdt_config_tab IS TABLE OF t_pdt_config_rec
2 /
Type created
SQL> CREATE OR REPLACE TYPE t_list_conf_rec IS OBJECT(
2 pdt_conf t_pdt_config_rec, -- product configuration and info
3 pdt_childs t_pdt_config_tab) -- similar or same products cobinations
4 /
Type created
SQL> CREATE OR REPLACE TYPE t_list_conf_tab IS TABLE OF t_list_conf_rec
2 /
Type created
Accessing sub-objects in PL/SQL is not unlike java:
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 l_conf_1 t_pdt_config_rec := t_pdt_config_rec('conf 1','A');
3 l_conf_2 t_pdt_config_rec := t_pdt_config_rec('conf 2','B');
4 l_child_1 t_pdt_config_rec := t_pdt_config_rec('conf 1 old', 'AA');
5 l_child_2 t_pdt_config_rec := t_pdt_config_rec('conf 1 old old','AB');
6 l_children_1 t_pdt_config_tab := t_pdt_config_tab(l_child_1, l_child_2);
7 l_children_2 t_pdt_config_tab := t_pdt_config_tab();
8 l_obj_1 t_list_conf_rec := t_list_conf_rec(l_conf_1, l_children_1);
9 l_obj_2 t_list_conf_rec := t_list_conf_rec(l_conf_2, l_children_2);
10 l_tab t_list_conf_tab := t_list_conf_tab(l_obj_1, l_obj_2);
11 BEGIN
12 FOR cc IN (SELECT o.pdt_conf.pdt_mnemo pdt_mnemo,
13 o.pdt_conf.pdt_variant pdt_variant
14 FROM TABLE(l_tab) o
15 WHERE o.pdt_conf.pdt_mnemo = 'conf 1'
16 AND o.pdt_conf.pdt_variant = 'A') LOOP
17 dbms_output.put_line('record found');
18 END LOOP;
19 END;
20 /
record found
PL/SQL procedure successfully completed
Before changes there was only t_pdt_config_tab and I have no
problems.
Of course.
You now have a NESTED array. so your outer TABLE(p_table) will be selecting the rows of t_crt_list_prdt_conf_tab (whatever that is, did you mean to type t_list_conf_tab??).
ill answer assuming you meant t_list_conf_tab and not t_crt_list_prdt_conf_tab. if t_crt_list_prdt_conf_tab is a type that contains t_list_conf_tab, then you'll need another level:
select list_conf.id list_conf_id,
list_conf.pdt_conf.pdt_grp_mnemo,
list_conf.pdt_conf.pdt_mnemo,
list_conf.pdt_conf.pdt_name,
list_conf.pdt_conf.pdt_variant,
list_conf.pdt_conf.det_info_xsr_id ,
list_conf.pdt_conf.det_info_view_template_name ,
list_conf.pdt_conf.det_info_download_xsl_id,
list_conf.pdt_conf.det_info_ctrl_url,
list_conf.pdt_conf.det_info_ctrl_action,
list_conf.pdt_conf.create_ctrl_url,
list_conf.pdt_conf.create_ctrl_action,
list_conf.pdt_conf.change_contract_name_enabled,
list_conf.pdt_conf.period_selector,
list_conf.pdt_conf.period_selector_hide_all_opt,
pdt_child.pdt_grp_mnemo,
pdt_child.pdt_mnemo,
pdt_child.pdt_name,
pdt_child.pdt_variant,
pdt_child.det_info_xsr_id,
pdt_child.det_info_view_template_name,
pdt_child.det_info_download_xsl_id,
pdt_child.det_info_ctrl_url,
pdt_child.det_info_ctrl_action,
pdt_child.create_ctrl_url,
pdt_child.create_ctrl_action,
pdt_child.change_contract_name_enabled,
pdt_child.period_selector,
pdt_child.period_selector_hide_all_opt,
pdt_child.downloads
from (SELECT rownum id,
pdt_conf,
pdt_childs
FROM TABLE(p_table)) list_conf,
table(list_conf.pdt_childs) pdt_child;
sql fiddle example: http://sqlfiddle.com/#!4/2eee6/1

COUNT function for files?

Is it possible to use COUNT in some way that will give me the number of tuples that are in a .sql file? I tried using it in a query with the file name like this:
SELECT COUNT(*) FROM #q65b;
It tells me that the table is invalid, which I understand because it isn't a table, q65b is a file with a query saved in it. I'm trying to compare the number of rows in q65b to a view that I have created. Is this possible or do I just have to run the query and check the number of rows at the bottom?
Thanks
You can do this in SQL*Plus. For example:
Create the text file, containing the query (note: no semicolon!):
select * from dual
Save it in a file, e.g. myqueryfile.txt, to the folder accessible from your SQL*Plus session.
You can now call this from within another SQL query - but make sure the # as at the start of a line, e.g.:
SQL> select * from (
2 #myqueryfile.txt
3 );
D
-
X
I don't personally use this feature much, however.
Here is one approach. It's a function which reads a file in a directory, wraps the contents in a select count(*) from ( .... ) construct and executes the resultant statement.
1 create or replace function get_cnt
2 ( p_file in varchar2 )
3 return number
4 as
5 n pls_integer;
6 stmt varchar2(32767);
7 f_line varchar2(255);
8 fh utl_file.file_type;
9 begin
10 stmt := 'select count(*) from (';
11 fh := utl_file.fopen('SQL_SCRIPTS', p_file, 'R');
12 loop
13 utl_file.get_line(fh, f_line );
14 if f_line is null then exit;
15 elsif f_line = '/' then exit;
16 else stmt := stmt ||chr(10)||f_line;
17 end if;
18 end loop;
19 stmt := stmt || ')';
20 execute immediate stmt into n;
21 return n;
22* end get_cnt;
SQL>
Here is the contents of a sql file:
select * from emp
/
~
~
~
"scripts/q_emp.sql" 3L, 21C
And here is how the script runs:
SQL> select get_cnt ('q_emp.sql') from dual
2 /
GET_CNT('Q_EMP.SQL')
--------------------
14
SQL>
So it works. Obviously what I have posted is just a proof of concept. You will need to include lots of error handling for the UTL_FILE aspects - it's a package which can throw lots of exceptions - and probably some safety checking of the script that gets passed.