WITH Clause inside PL-SQL - plsql

I am trying to use 'WITH' clause inside PL-SQL block :
Cursor using WITH clause as follows :
CURSOR c_API_MSG
IS
WITH SAMI AS (SELECT * FROM NAGENDRA WHERE STATUS = 'NEW')
SELECT * FROM SAMI WHERE ROWNUM <= TO_NUMBER (10);
Execution :
FOR v_Rec IN c_API_MSG
LOOP
BEGIN
-- My LOGIC
END;
END LOOP;
It is not executing properly. It is not going inside loop. seems like cursor not able to fetch rows & that's why exiting.
Normal sub-query without with clause working fine. With clause working fine on editor.
Is there any limitation of using 'WITH' clause or am I missing something here ?
Observations :
Inside toad editor with below query :
If I Use f9 (normal execute) : 5 rows found (Correct result)
If I use f5 : No rows found (This is I am worried about)
WITH SAMI AS (SELECT * FROM NAGENDRA WHERE STATUS = 'NEW')
SELECT * FROM SAMI WHERE ROWNUM <= TO_NUMBER (10);

Hey Yes PLSQL supports "WITH" clause. Plz see below snippet.
SET sqlbl ON;
SET serveroutput ON;
DECLARE
CURSOR LV_CUR
IS
WITH
TEMP_AV AS
(
SELECT
LEVEL
FROM
DUAL
CONNECT BY LEVEL < 10
)
SELECT
*
FROM
TEMP_AV;
BEGIN
FOR I IN LV_CUR
LOOP
NULL;
dbms_output.put_line(i.level);
END LOOP;
END;

Related

Oracle SQL Developer - Using Variables in Insert/Select

Using Oracle SQL Developer. I have the following sample working:
DECLARE
v_next_TransId INTEGER; -- declare
BEGIN
select (max(Transaction_ID)+1) into v_next_TransId from xxpos.pos_transactions;
dbms_output.Put_line(v_next_TransId); --display
END;
Now I want to use that variable in an insert and/or select, I tried with and without a : prefix. The sample below gives me the ORA-01008: not all variables found.
DECLARE
v_next_TransId INTEGER; -- declare
BEGIN
select (max(Transaction_ID)+1) into v_next_TransId from xxpos.pos_transactions;
dbms_output.Put_line(v_next_TransId); --display
-- insert new row will go here
-- commit will go here
-- now verify the insert worked okay
--select * from xxpos.pos_transactions where Transaction_ID = ( select max(Transaction_ID) from xxpos.pos_transactions )
select * from xxpos.pos_transactions where Transaction_ID = :v_next_TransId;
END;
Without the : in front of the variable, I get this syntax error:
Versions:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
In PLSQL code block, columns of select statement have to be assigned to variables. You have to use select into clause your plsql code block as shown below.
DECLARE
v_next_TransId INTEGER; -- declare
l_pos_transactions xxpos.pos_transactions%ROWTYPE;
BEGIN
select (max(Transaction_ID)+1) into v_next_TransId from xxpos.pos_transactions;
dbms_output.Put_line(v_next_TransId); --display
-- insert new row will go here
-- commit will go here
-- now verify the insert worked okay
--select * from xxpos.pos_transactions where Transaction_ID = ( select max(Transaction_ID) from xxpos.pos_transactions )
select * into l_pos_transactions
from xxpos.pos_transactions where Transaction_ID = :v_next_TransId;
DBMS_OUTPUT.PUT_LINE(l_pos_transactions.column1|| ',' || l_pos_transactions.column2);
END;

Does Oracle support non-scalar cursor parameter?

This is a question about Oracle PL/SQL.
I have a procedure in which the exact WHERE clause is not known until the run time:
DECLARE
CURSOR my_cursor is
SELECT ...
FROM ...
WHERE terms in (
(SELECT future_term2 FROM term_table), -- whether this element should be included is conditional
(SELECT future_term1 FROM term_table),
(SELECT present_term FROM term_table)
);
BEGIN
(the processing)
END;
/
What the (SELECT ... FROM term_table) query returns is a 4-character string.
For a solution to this, I am thinking of using a parameterized cursor:
DECLARE
target_terms SOME_DATATYPE;
CURSOR my_cursor (pi_terms IN SOME_DATATYPE) IS
SELECT ...
FROM ...
WHERE terms in my_cursor.pi_terms;
BEGIN
target_terms := CASE term_digit
WHEN '2' THEN (
(SELECT future_term2 FROM term_table),
(SELECT future_term1 FROM term_table),
(SELECT present_term FROM term_table)
) ELSE (
(SELECT future_term1 FROM term_table),
(SELECT present_term FROM term_table)
)
END;
FOR my_record IN my_cursor (target_terms) LOOP
(the processing)
END LOOP;
END;
/
The problem is what the datatype for SOME_DATATYPE should be is not known to me, nor is it known whether Oracle supports such a cursor parameter at all. If supported, is the way shown above to fabricate the value for target_terms correct? If not, how?
Hope someone who know can advise. And thanks a lot for the help.
You can certainly pass a parameter to a cursor, just like you can to a function - but only IN parameters. However, PL/SQL is a strongly typed language, so the datatype must be specified at the time of compilation.
It looks to me like what you will need to do is construct the query dynamically and then use
OPEN cursor FOR l_query;
where l_query is the constructed string. This should give you a feel for what you can do:
CREATE OR REPLACE PACKAGE return_id_sal
AUTHID DEFINER
IS
TYPE employee_rt IS RECORD
(
employee_id employees.employee_id%TYPE,
salary employees.salary%TYPE
);
FUNCTION allrows_by (append_to_from_in IN VARCHAR2 DEFAULT NULL)
RETURN SYS_REFCURSOR;
END return_id_sal;
/
CREATE OR REPLACE PACKAGE BODY return_id_sal
IS
FUNCTION allrows_by (append_to_from_in IN VARCHAR2 DEFAULT NULL)
RETURN SYS_REFCURSOR
IS
l_return SYS_REFCURSOR;
BEGIN
OPEN l_return FOR
'SELECT employee_id, salary FROM employees ' || append_to_from_in;
RETURN l_return;
END allrows_by;
END return_id_sal;
/
DECLARE
l_cursor SYS_REFCURSOR;
l_row return_id_sal.employee_rt;
BEGIN
l_cursor := return_id_sal.allrows_by ('WHERE department_id = 10');
LOOP
FETCH l_cursor INTO l_row;
EXIT WHEN l_cursor%NOTFOUND;
END LOOP;
END;
/
You will need to take precautions against SQL injection with this sort of code. Certainly a user should never be able to pass SQL text directly to such a function!
You can use also some built-in VARRAY SQL types like SYS.ODCIVARCHAR2LIST or create your own :
CREATE OR REPLACE NONEDITIONABLE TYPE VARCHARLIST
AS VARRAY(32767) OF VARCHAR2(4000);
Then you can use it with SELECT COLUMN_VALUE FROM TABLE(COLLECTION) statement in your cursor:
DECLARE
l_terms SYS.ODCIVARCHAR2LIS; --or VARCHARLIST
CURSOR my_cursor (p_terms IN SYS.ODCIVARCHAR2LIS) IS
SELECT your_column
FROM your_table
WHERE terms in (select COLUMN_VALUE from table (p_terms));
BEGIN
select term
bulk collect into l_terms
from (
select 'term1' term from dual
union all
select 'term2' term from dual
);
FOR my_record IN my_cursor (l_terms) LOOP
--process data from your cursor...
END LOOP;
END;

dynamically change column in oracle cursor

I have 50 columns in one table and need to update each column.
Trying the below plsql code. (commented update section is working).
But dynamically generated column is not accepting.
(PL/SQL: ORA-01747: invalid user.table.column, table.column, or column specification)
Anybody can help please?
DECLARE
cursor udas is
select 5109 as udaid from dual
union all
select 8209 as udaid from dual;
BEGIN
for uda in udas loop
DECLARE
cursor c1 is
select
x.item, x.uda_id, x.uda_value, x.uda_value_desc
from
hp2_uda_data x
where
x.uda_type='LOV'
and x.uda_id=uda.udaid;
begin
for i in c1 loop
begin
/*update testtable set item_uda_5109_v=i.uda_value,
item_uda_5109_d=i.uda_value_desc where item_code=i.item;*/
update testtable set 'item_uda_'||uda.udaid||'_v'=i.uda_value,
'item_uda_'||uda.udaid||'_d'=i.uda_value_desc where item_code=i.item;
end;
end loop;
commit;
end;
end loop;
END;
Dynamic code requires execute immediate:
execute immediate
'update testtable
set item_uda_'||uda.udaid||'_v = :b1
, item_uda_'||uda.udaid||'_d = :b2
where item_code = :b3'
using i.uda_value, i.uda_value_desc, i.item;
It can be useful to construct the dynamic code in a variable so that you can report or log it in the event of failure.
I also recommend looking into code indentation as a useful technique for making code readable.

how to use Trunc(mydate) in a cursor loop

I am new to PL/SQL, and am working on cursor today and got a scenario where I need to get the duplicate transaction type based on the date+amount+cust_id+txn_tpye. Once I get the duplicates I have to use another cursor or just normal loop using the select columns values (date+amount+cust_id+txn_tpye) as a where clause.
Before that I'm just trying to print them if I am getting a value or not, when I tried to print a mydate value getting error. Requesting help from you folks.
SET SERVEROUTPUT ON
declare
CURSOR dup_check
IS
SELECT cust_id,amount,trunc(mydate),transaction_type,COUNT(1)
FROM table_X WHERE trunc(mydate)>='10-OCT-2015'
GROUP BY cust_id,amount,trunc(mydate),transaction_type
HAVING COUNT(1)>1 ;
BEGIN
FOR UP_REC IN dup_check
LOOP
DBMS_OUTPUT.put_line(UP_REC.cust_id||' '||UP_REC.amount||UP_REC.trnasaction_type||**trunc(mydate))**;
END LOOP;
END;
**PLS-00302: component 'mydate' must be declared**
add alias for trunc(mydate) field as below and put the UP_REC.mydate in your dbms_output
SET SERVEROUTPUT ON
declare
CURSOR dup_check
IS
SELECT cust_id,
amount,
trunc(mydate) mydate, /* add an alias here */
transaction_type,
COUNT(1)
FROM table_X WHERE trunc(mydate) >= '10-OCT-2015'
GROUP BY cust_id,amount,trunc(mydate),transaction_type HAVING COUNT(1)>1 ;
BEGIN
FOR UP_REC IN dup_check
LOOP
DBMS_OUTPUT.put_line(UP_REC.cust_id||' '||UP_REC.amount||UP_REC.trnasaction_type||UP_REC.mydate));
END LOOP;
END;

PL/SQL Blocks - seeing output simply ? [A Very simple question I'm sure !]

I'm sure what I want is very simple but I cannot figure out how.
I want :
To declare some variables and initialize them to certain values
To excecute a number of selects (predicated by the above variable values) and see the results as if i had executed the results straight on the sqlplus command line
I believe it's necessary to use the block structure in order that I may declare and make use of variables within the predicates of the queries. Although the examples shown here are quite simple in the real case there are numberous, much more complex SELECT's.
I tried doing this (forgetting about predicates for a moment) ...
DECLARE
EMP_EMPLOYEE_ID_IN VARCHAR2(12);
BEGIN
EXECUTE IMMEDIATE 'SELECT * FROM DEPT WHERE DEPNO';
END;
/
... but when I do that I get to execute the select without seeing the output.
I've also tried this ...
DECLARE
EMP_EMPLOYEE_ID_IN VARCHAR2(12);
BEGIN
SELECT * FROM DEPT;
END;
/
... but then I get ...
PLS-00428: an INTO clause is expected in this SELECT statement
... I really don't want to have to declare a variable for every column which would appear in my output.
Can anyone tell me how I can execute the SELECTs but simply and easily see the output as if I were on the sqlplus command line, ie to see the same output as if I did this
SQL> SELECT * FROM DEPT;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
Thanks
I have now tested the answer given by Shannon Severance below and found that it will do what I want.
For the sake of later readers I thought it might be useful to show the complete script here.
set line 32000;
set trimspool on;
var V_CURSOR1 REFCURSOR;
var V_CURSOR2 REFCURSOR;
var V_CURSOR3 REFCURSOR;
DECLARE
DEPT_NUM_IN VARCHAR2(12);
BEGIN
DEPT_NUM_IN := '10';
OPEN :V_CURSOR1 FOR SELECT * FROM DEPT;
OPEN :V_CURSOR2 FOR SELECT * FROM DEPT ORDER BY LOC;
OPEN :V_CURSOR3 FOR SELECT * FROM DEPT WHERE DEPTNO = DEPT_NUM_IN ORDER BY LOC;
END;
/
print V_CURSOR1
print V_CURSOR2
print V_CURSOR3
From sqlplus, other tools may be different.
First declare a sqlplus refcursor variable
SQL> var l_cursor refcursor
Then open that cursor within a PL/SQL block, where you will have access to declared variables and everything:
SQL> edit
Wrote file afiedt.buf
1 declare
2 l_number number;
3 begin
4 open :l_cursor for select table_name from all_tables where rownum < 10;
5* end;
SQL> /
PL/SQL procedure successfully completed.
Notice above that the refcursor variable is prepended with a :, this is because we are binding a sqlplus variable into the PL/SQL anonymous block
Next, use the SQLPLUS print command:
SQL> print l_cursor
TABLE_NAME
------------------------------
ICOL$
CON$
UNDO$
PROXY_ROLE_DATA$
FILE$
UET$
IND$
SEG$
COL$
9 rows selected.
DECLARE
v_result VARCHAR2(400);
BEGIN
SELECT dummy
INTO v_result
FROM dual;
dbms_output.put_line(v_result);
END;

Resources