Oracle Object inside object inside table - sql

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

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>

Wrong number or type in a loop

getting this error:
ERROR at line 5:
ORA-06550: line 5, column 19:
PLS-00306: wrong number or types of arguments in call to 'DAYS'
So it's an issue with that bolded "days" ...why?
CREATE OR REPLACE
TYPE lyrics IS OBJECT
( date_name VARCHAR2(8)
, gift_name VARCHAR2(24));
/
DECLARE
TYPE days IS TABLE OF lyrics;
TYPE gifts IS TABLE OF lyrics;
lv_dates DAYS :=
**days**('first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth', 'eleventh', 'twelth');
That would be something like this; elements inside the array must be "objects", so you have to specify lyrics within the code:
SQL> create or replace
2 type lyrics is object
3 ( date_name varchar2(8)
4 , gift_name varchar2(24));
5 /
Type created.
SQL>
SQL> declare
2 type days is table of lyrics;
3 lv_dates days := days();
4 begin
5 lv_dates := days(lyrics('first', 'second'), lyrics('third', 'fourth'));
6 end;
7 /
PL/SQL procedure successfully completed.
SQL>

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.

ORACLE: Parameter reference in WHERE doesn't work

I have created a simple static function in oracle 10g to get the reference of an object based on his pk.
STATIC FUNCTION getRef(nome IN VARCHAR2)
RETURN REF folder_typ IS
fl_r REF folder_typ := null;
BEGIN
SELECT REF(fl)
INTO fl_r
FROM folder_tab fl
WHERE fl.nome = nome;
RETURN fl_r;
END getRef;
This gives me an error because it could't fetch a row.
If insted of WHERE fl.nome = nome; I write WHERE fl.nome = 'folder1'; -- it works.
I think im not using the parameter in the right way.
How can I use it?
This is a scoping problem.
Here is my version of your set-up, extrapolated from what you posted. Note that the parameter name is different from the attribute name.
create or replace type folder_t as object
(name varchar2(128))
/
create table folders of folder_t
/
create or replace function getRef
(nome in varchar2)
return ref folder_t
is
fl_r REF folder_t;
begin
select ref(fl)
into fl_r
from folders fl
where fl.name = nome;
return fl_r;
end getRef;
/
As you can see, given this test data ...
SQL> insert into folders values (folder_t('temp'))
2 /
1 row created.
SQL> insert into folders values (folder_t('work'))
2 /
1 row created.
SQL>
... I can query the two REFs:
SQL> select getRef('temp') from dual
2 /
GETREF('TEMP')
--------------------------------------------------------------------------------
00002802091051432318864AF594741916D743E1291CF597373A4F4D7A93F159DA53A73FC0010372
2D0000
SQL> select getRef('work') from dual
2 /
GETREF('WORK')
--------------------------------------------------------------------------------
0000280209F31778C18D5740FBA0CB90929E1B6FBD1CF597373A4F4D7A93F159DA53A73FC0010372
2D0001
SQL>
But, if I change the function declaration so the parameter name is the same as the attribute name, this happens:
SQL> create or replace function getRef
2 (name in varchar2)
3 return ref folder_t
4 is
5 fl_r REF folder_t;
6 begin
7 select ref(fl)
8 into fl_r
9 from folders fl
10 where fl.name = name;
11
12 return fl_r;
13 end getRef;
14 /
Function created.
SQL> select getRef('temp') from dual
2 /
GETREF('TEMP')
------------------------------------------------------------
SQL>
The SQL engine applies scope from the table outwards. Because the unqualified NAME matches a column on the table it doesn't check to see that whether there is a parameter of that name too. This is why it is a good idea to give the parameters a distinct name. Persoanlly I favour the practice of prefixing parameters with a P_, so there's no chance of the paremeter clashing with local variables or object names.
Best practice is to always provide the scope for all variables, not just for those from table aliases:
FUNCTION getRef(nome IN VARCHAR2)
RETURN REF folder_typ IS
fl_r REF folder_typ := null;
BEGIN
SELECT REF(fl)
INTO fl_r
FROM folder_tab fl
WHERE fl.nome = getRef.nome;
RETURN fl_r;
END getRef;