I want to use decode function in cursor with alias names to avoid column ambiguity so i used below approach.
I have code such as:
declare
cl number;
cursor c is
select c1.rowid,c1.col1,
DECODE(c1.col2, 'XYZ', c1.col3, 10) cl
from table1 d,table2 c1 where c1.process_id=13525 and d.col3(+)=cl;
begin
for rec in c
loop
dbms_output.put_line(NVL(rec.cl,'-1'));
end loop;
end;
In this, when i will fire query by removing condition 'and d.col3(+)=cl' it will retrieve me data with the value of 'cl' . But when i assign this condtion it will not retrive data and not go in for loop of cursor.I have a matching data in d.col3.
Suppose if i will get cl as 5 then it is also present in d.col3 then it should give me data i did this because i need to remove duplicate records.Because with that single condition i will get duplicate records.Here col3 in d table is as primary key.
So i am not getting why it will not go in for loop as it gets value from query.
You can't use alias in WHERE clause: Using an Alias in a WHERE clause
In such cases, a sub-query or a CTE might help. Something like that (untested!):
with V as (
select c1.rowid rid, ,c1.col1, c1.process_id,
DECODE(c1.col2, 'XYZ', c1.col3, 10) cl
from table2 c1)
select V.rid, V.col1, V.cl from table1 d,V
where V.process_id=13525 and d.col3(+)=V.cl;
After getting suggestion that using WITH clause, My approach to retrieve data through DECODE() using alternative table name is:
declare
cl number;
cursor c is
with V as (
select c1.process_id,
DECODE(c1.col2, 'BANDM', c1.col3, 10) cl
from table2 c1)
select c1.rowid rid,c1.col1, V.cl from table1 d,V,table2 c1
where V.process_id=1
and d.col3(+)=V.cl
and c1.col3=V.cl;
begin
for rec in c
loop
dbms_output.put_line(NVL(rec.rid,'-1'));
dbms_output.put_line(NVL(rec.cl,'-1'));
end loop;
end;
Another solution without WITH clause is :
declare
c2 number;
cursor c is
select c1.process_id
c1.rowid,
c1.col1,
DECODE(c1.col2, 'BANDM', c1.col3, 10) as c2
from table1 d,
table2 c1
where c1.process_id=1
and d.col3(+) = DECODE(c1.col2, 'BANDM', c1.col3, 10);
begin
for rec in c
loop
dbms_output.put_line(NVL(rec.rid,'-1'));
dbms_output.put_line(NVL(rec.c2,'-1'));
end loop;
end;
Related
select count(*)
INTO countExceed
from uid_emp_master k
where k.unique_id in (select k.reviewer_uid
from uid_rm_hierarchy k
where k.unique_id in ('||p_ID_list||'))
and k.band IN( 'A','B','C','D');
if (countExceed > 0) then
quer :='UPDATE UID_RM_HIERARCHY I
SET I.REVIEWER_UID in (SELECT L.REVIEWER_UID
FROM UID_RM_HIERARCHY L
WHERE L.UNIQUE_ID in ('||p_ID_list||') )
WHERE I.REVIEWER_UID in('||p_ID_list||')
and isdeleted=0';
EXECUTE IMMEDIATE quer ;
END IF;
the above stored procedure does not show any result the variable countExceed declared as a number please help me to correct the query.
The issue is in
where k.unique_id in ('||p_ID_list||'))
Here you are saying to look for records
where unique_id = '||p_ID_list||'
exactly as its typed, but what you need is to handle that variable as a list of values.
Say you have a table like this
create table tabTest(id) as (
select 'id1' from dual union all
select 'id2' from dual union all
select 'id3' from dual union all
select 'id4' from dual
)
and your input string is 'id1,id3,1d8';
I see two ways to do what you need; one is with dynamic SQL, for example:
declare
vResult number;
vList varchar2(199) := 'id1,id3,1d8';
vSQL varchar2(100);
begin
vSQL :=
'select count(*)
from tabTest
where id in (''' || replace (vList, ',', ''', ''') || ''')';
--
execute immediate vSQL into vResult;
--
dbms_output.put_line('Result: ' || vResult);
end;
Another way could be by splitting the string into a list of values and then simply using the resulting list in the IN.
For that, there are many answers about how to split a comma separated list string in Oracle.
Let me start off by saying I'm open to other ways of doing this, but right now, this is all I've been able to come up with.
I'm working within a package.procedure and I am using a table type as an array/list to store IDs of records that match search criteria. Once I have compiled the list, I want to open a cursor that selects from a table where the record ID is in my list.
Data structure:
TYPE i_array IS TABLE OF t_table.id_column%TYPE INDEX BY PLS_INTEGER;
lt_results i_array;
ln_count pls_integer;
Populating list:
ln_count := 0;
for recs in (select id_column from t_child where other_column = ls_criteria)
loop
ln_count := ln_count + 1;
lt_results(ln_count);
end loop;
Opening cursor and accessing list:
open cur for
select col_a,
col_b,
col_c
from t_table
where id_column in (select lt_results(level)
from dual
connect by level <= ln_count);
If using Oracle 12C you can use a nested table collection in the package like this:
create or replace package test is
TYPE i_array IS TABLE OF t_child.id_column%TYPE;
procedure run;
end;
create or replace package body test is
procedure run is
lt_results i_array := i_array();
cur sys_refcursor;
begin
for r in (select id_column from t_child where other_column = ls_criteria) loop
lt_results.extend;
lt_results(lt_results.count) := r.id_column ;
end loop;
open cur for
select col_a,
col_b,
col_c
from t_table
where id_column in (select column_value from table(lt_results));
end run;
end;
Prior to 12C the type i_array would need to be in the database:
create type i_array is table of varchar2(10); -- For example
You could use a handy pre-existing collection like SYS.KU$_VCNT instead of creating your own.
BTW Note that this:
for r in (select id_column from t_child where other_column = ls_criteria) loop
lt_results.extend;
lt_results(lt_results.count) := r.id_column ;
end loop;
can be replaced more efficiently by this:
select id_column
bulk collect into lt_results
from t_child where other_column = ls_criteria;
I have two tables T1 and T2
Senario 1:
Table T1 has a Column C1 with values terminal1, terminal2, terminal3
Select C1 from T1
Terminal1
Terminal2
Terminal3
Table T2 has a Column C2 with values terminal1, terminal2
Select C2 from T2
Terminal1
Terminal2
Senario 2
Table T2 can sometimes be null
Select C2 from T2
(null)
(null)
My Output has to be
When Senario 1 Then
Terminal1
Terminal2
When Senario 2 Then
Terminal1
Terminal2
Terminal3
i.e when table T1 and T2 have common values I want only the matching values
and
when Table T2 has null values then I want all the values
from table T1
It seems that what you need is a left outer join. Outer joins allow us to conditionally join tables.
This version of your query will return STORE_TERMINAL_LOCATION for all the rows in SP_RETAIL_TRANSACTION and matching values only of SP_TEMP_LOOKUP.AIRPORT_TERMINAL.
SELECT DISTINCT A.STORE_TERMINAL_LOCATION, B.AIRPORT_TERMINAL
FROM SP_RETAIL_TRANSACTION A,
left outer join SP_TEMP_LOOKUP B
on A.STORE_TERMINAL_LOCATION = B.AIRPORT_TERMINAL
Your question needs some proper formatting and elaborate more on the problem you are facing. I assume, you need a statement, which shows what values intersect, and if they do not - show only tables A. For that clause, you need to use inner joins and case structure. For example:
select
ter."HEATHROW_TERMINAL",
case
when lok."HEATHROW_TERMINAL" is not null then
'Terminal is in both tables'
else
'Terminalis only in main table'
end terminal_state
from
test_terminal_v ter,
test_lookup_v lok
where
ter."HEATHROW_TERMINAL" = lok."HEATHROW_TERMINAL"(+)
wheres, test_terminal_v (A) has values "5","4","2","3" and test_lookup_v (B) has "4","2","3".
Hope it helps.
After comments: there are several approaches. 1) You can use temporary tables. Create one and the code is:
begin
insert into test_term_tmp
select
ter.terminal
from
test_terminal_v ter,
test_lookup_v lok
where
ter.terminal = lok.terminal;
if sql%rowcount = 0 then
insert into TEST_TERM_TMP
select
ter.terminal
from
test_terminal_v ter;
end if;
end;
or 2) use types, for example:
declare
type t_term_tab is table of number index by binary_integer;
l_term t_term_tab;
l_i number := 1;
begin
for l_rec1 in (
select
ter.terminal
from
test_terminal_v ter,
test_lookup_v lok
where
ter.terminal = lok.terminal)
loop
l_term(l_i) := l_rec1.terminal;
l_i := l_i + 1;
end loop;
if l_term.first is null then
l_i := 1;
for l_rec2 in (
select
ter.terminal
from
test_terminal_v ter)
loop
l_term(l_i) := l_rec2.terminal;
l_i := l_i + 1;
end loop;
end if;
end;
Test the performance, based on your data. Hope it helps.
Thank you Guys, I think I found the answer
select distinct T1.C1, T2.C2
from T1 ,T2
where T2.C2=T1.C1 or T2.C2 is null;
Where in my code it was
select distinct A.STORE_TERMINAL_LOCATION,B.AIRPORT_TERMINAL
from SP_RETAIL_TRANSACTION A,SP_TEMP_LOOKUP B
where B.AIRPORT_TERMINAL=A.STORE_TERMINAL_LOCATION or B.AIRPORT_TERMINAL is null;
I have three PLSQL functions: A, B and C.
The idea is this: C is calling B, B is calling A.
Function A, when it's called by B, returns a numeric value as a status indicator AND a ref cursor with tabular results.
E.g. function_A (A1 in varchar2, A2 out sys_refcursor) return number;
Function B, when it receives the results from A, is expected to reformat the results before passes them on to C, also in a form of a ref cursor.
A is an existing function and it cannot be amended, while B and C will be completely new functions.
The question is, how do I fetch the ref cursor from A? I was able to get the numeric value returned by the function (i.e the status indicator), but I have problem fetching the results of the ref cursor from A.
If I'm calling A from B, can I assume that the ref cursor of A is automatically opened?
What are the logical steps to get the results from A's ref cursor? E.g. can I fetch the results into an object type?
P/S. I have very limited programming experience and am only few months new in PLSQL.
Any hints will be much appreciated.
Since you have not given us the code functions, we will be based on the description of your functions.
According to the description, you have 3 functions:
Function A.
create or replace function A(A1 in varchar2, A2 out sys_refcursor) return number is
begin
open A2 for select 1 from dual;
return 2;
end;
Function B.
create or replace function B(B1 out sys_refcursor) return number is
cur sys_refcursor;
res_A number;
row_ your_table_a%rowtype;
begin
res_A := A('',cur);
loop
fetch cur into row_;
exit when cur%notfound;
--proccess with row A
end loop;
open B1 for select 2 from dual;
return 1;
end;
Function C
create or replace function C() return number is
res_B number;
cur sys_refcursor;
row_ your_table_b%rowtype;
begin
res_B:= B(cur);
loop
fetch cur into row_;
exit when cur%notfound;
--proccess with row B
end loop;
return 2;
end;
Maybe you can try the below snippet. Tried to replicate the scenario you mentioned in the question. Hope this helps.
CREATE OR REPLACE FUNCTION A_TEST(
A1 IN VARCHAR2,
A2 OUT sys_refcursor )
RETURN NUMBER
AS
lv_num PLS_INTEGER;
BEGIN
NULL;
OPEN a2 FOR SELECT LEVEL FROM DUAL CONNECT BY LEVEL < 19;
RETURN 1;
END;
CREATE OR REPLACE FUNCTION B_TEST
RETURN sys_refcursor
AS
lv_cur sys_refcursor;
lv_num PLS_INTEGER;
BEGIN
lv_num:=A_TEST('AV',lv_cur);
RETURN lv_cur;
END;
CREATE OR REPLACE FUNCTION C_TEST
RETURN sys_refcursor
AS
tab PLS_INTEGER;
lv_cur sys_refcursor;
BEGIN
lv_cur:=B_TEST;
LOOP
FETCH lv_cur INTO tab;
EXIT
WHEN lv_cur%NOTFOUND;
dbms_output.put_line(tab);
END LOOP;
END;
I am passing c1 value as param to the c2,c3 cursor's , so i am getting duplicate values and how to write better than this code using plsql code?
Declare
cursor cur1;
cursor cur2
is
select * from
where param=c1.param;
cursor cur3
is
select * from
where param=c1.param;
Begin
for c1 loop
for c2(c1.param)
dbms_output(deptno||' '||dname);
for c3(c1.param)
dbms_output(deptno||' '||dname);
end loop;
end loop;
end loop;
End;
So , i am getting duplicate values
deptno dname
10 a
20 b
30 c
10 a
20 b
30 c
Expected output as
deptno dname
10 a
20 b
30 c
can you please help me?
Sounds like you may want a UNION:
Declare
cursor cur1;
cursor cur2 is
select * from X
where param=c1.param
union
select * from Y
where param=c1.param;
Begin
for c1 loop
for c2(c1.param)
dbms_output(deptno||' '||dname);
end loop;
end loop;
End;
(I haven't fixed your invalid code above - presumably your real code is correct or it wouldn't have run at all.)
You probably don't need even 2 cursors, you could do something like:
Declare
cursor cur is
select * from X
where param in (select ...)
union
select * from Y
where param in (select ...)
Begin
for c2 in cur loop
dbms_output(deptno||' '||dname);
end loop;
End;
you can use inner join to avoid cursor looping.
use distinct keyword to get distinct value.
user of rownum keword to get only one row.
eliminate duplicate by group by clause.