creating cursor for counting - oracle

I have this procedure :
create or replace procedure check_max_game_rule_test (i_player_id IN NUMBER,i_max_games IN NUMBER, i_err_code OUT number)
is
v_fixid number;
v_count_fixid number;
max_games_violation exception;
cursor c1 is
SELECT ti.fixid,count(ti.fixid)
FROM BUS_SESSION BS,
CASINO_USERS CUP,
TICKET_ITEMS TI
WHERE BS.PARTY_ID = 4356
AND TI.BUS_SESSION_SESSION_ID = BS.SESSION_ID
AND NVL(BS.SESSION_CLOSE,'N') = 'N'
and CUP.parent_id = BS.AFF_ID
AND BS.SESSION_TYPE = 'TICKET SESSION'
group by TI.FIXID;
r1 c1%ROWTYPE;
begin
open c1;
loop
fetch c1 into v_fixid,v_count_fixid;
if v_count_fixid > i_max_games
then
raise max_games_violation;
end if;
end loop;
close c1;
end ;
the results which select returns is :
Procedure should count same fixid's and compare it to i_max_games and if count of that fixid is larger than i_max_games then raise exception..
Now i m getting encountered symbol c1
Can someone help me about this and is this cursor for getting and comparing fixid logical ?

You don't need a CURSOR loop for this. use a single select and raise exception based on the total count. Also, use ANSI JOINs.
create or replace procedure check_max_game_rule_test (i_player_id in number,
i_max_games in number,
i_err_code out number)
is
v_count_fixid number;
max_games_violation exception;
begin
select count(*)
into v_count_fixid
from (
select 1 FROM bus_session bs
join casino_users cup
on cup.parent_id = bs.aff_id
join ticket_items ti
on ti.bus_session_session_id = bs.session_id
where bs.party_id = 4356
and nvl(bs.session_close, 'N') = 'N'
and bs.session_type = 'TICKET SESSION'
group by ti.fixid
having count(ti.fixid) > i_max_games );
if v_count_fixid > 0 then
raise max_games_violation;
end if;
end;
/

Related

Oracle generic function to read different cursor

Is it possible to fetch cursor data into array or table with dynamic cursor parameters ?
Example : We have 3 function fct1(), fct2(), fct3(). They all return a CURSOR with different data (from different table) and their size is between 20 and 100.
I'd like to call this 3 function with a generic function, fill a VARCHAR2 array and for example print this array.
I've found out how to fetch cursor into a VARRAY or VARCHAR2 but then you need to specify which cursor value you add into the array.
CREATE OR REPLACE PACKAGE BODY CURSOR_EXAMPLE AS
FUNCTION fct1 (param01 IN VARCHAR2) RETURN SYS_REFCURSOR AS
my_cursor SYS_REFCURSOR;
BEGIN
OPEN my_cursor FOR
SELECT a, b, c FROM table1, table2, table3;
RETURN my_cursor;
END fct1;
FUNCTION fct2 (param01 IN VARCHAR2) RETURN SYS_REFCURSOR AS
my_cursor SYS_REFCURSOR;
BEGIN
OPEN my_cursor FOR
SELECT q, w, e, r, t, y FROM table4, table5;
RETURN my_cursor;
END fct2;
FUNCTION fct3 (param01 IN VARCHAR2, param02 IN VARCHAR2, param03 IN VARCHAR2) RETURN SYS_REFCURSOR AS
my_cursor SYS_REFCURSOR;
BEGIN
OPEN my_cursor FOR
SELECT x, y, z FROM table6, table7, table8, table8;
RETURN my_cursor;
END fct3;
PROCEDURE generic_function (fct IN NUMBER, param01 IN VARCHAR2, param02 IN VARCHAR2, param03 IN VARCHAR2, cursor_out OUT SYS_REFCURSOR)
AS
BEGIN
IF fct = 1
cursor_out := fct1(param01);
ELSE IF fct = 2
cursor_out := fct2(param01);
ELSE IF
cursor_out := fct3(param01, param02, param03);
END IF;
LOOP
FETCH cursor_out INTO
-- HERE DEPENDING ON fct1, 2 and 3 add cursor_out variable into a VARRAY (extend it as needed)
EXIT WHEN cursor_out%NOTFOUND;
END LOOP;
-- PRINT VARRAY
END generic_function;
END CURSOR_EXAMPLE;
It appears that you are trying to devise a dynamic means of discovering what columns are contained within the cursor.
There is a way, but it involves using DBMS_SQL and once you convert the cursor to a DBMS_SQL cursor, you will have to use DBMS_SQL all the way as you process the records.
Here is an example of a test function that can return one of four different cursors, without static defined types:
CREATE OR REPLACE FUNCTION get_test_cursor (n in number default 1) return SYS_REFCURSOR
IS
c SYS_REFCURSOR;
BEGIN
IF n = 1
THEN
OPEN c FOR SELECT 'A' f1, 'B' f2 FROM DUAL;
END IF;
IF n = 2
THEN
OPEN c FOR SELECT cast(1.234 as number(6,2)) n1, cast(12 as number(8)) n2, 'C' f3 FROM DUAL;
END IF;
IF n = 3
THEN
OPEN c FOR select * from all_objects;
END IF;
if n = 4
then
OPEN c FOR select sysdate as current_dt from dual;
end if;
return c;
END;
/
Now, we can use the DBMS_SQL package to first convert the cursor from a REF CURSOR to a cursor number using dbms_sql.to_cursor_number.
Once we do this, we can use the rest of the DBMS_SQL API to inspect the cursor and do work on its data.
declare
cur sys_refcursor;
c number;
col_cnt INTEGER;
rec_tab DBMS_SQL.DESC_TAB;
--
-- This is a crude helper procedure to display one line of "DESCRIBE" output
--
PROCEDURE print_rec(rec in DBMS_SQL.DESC_REC) IS
BEGIN
DBMS_OUTPUT.PUT_LINE(rpad(rec.col_name, 41) || ' ' ||
rpad(case when rec.col_null_ok then ' ' else 'NOT NULL' end, 8) || ' ' ||
case when rec.col_type = DBMS_TYPES.TYPECODE_VARCHAR or rec.col_type = DBMS_TYPES.TYPECODE_VARCHAR2 then
'VARCHAR2(' || rec.col_max_len || ')'
when rec.col_type = DBMS_TYPES.TYPECODE_CHAR then
'CHAR(' || rec.col_max_len || ')'
when rec.col_type = DBMS_TYPES.TYPECODE_NUMBER then
case when rec.col_precision = 0 and rec.col_scale = -127 then 'NUMBER'
when rec.col_scale = 0 then 'NUMBER('||rec.col_precision||')'
else 'NUMBER(' || rec.col_precision || ', ' || rec.col_scale || ')'
end
when rec.col_type = DBMS_TYPES.TYPECODE_DATE then
'DATE'
else
'UNKNOWN'
end);
END;
begin
cur := test_cursor(3);
--
-- Convert the REF_CUR to a cursor number
--
c := dbms_sql.to_cursor_number(cur);
--
-- Use an API call to describe the columns
--
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
--
-- Now loop through the columns and show them
--
dbms_output.put_line('Name Null? Type');
dbms_output.put_line('----------------------------------------- -------- ----------------------------');
for j in 1..col_cnt loop
print_rec(rec_tab(j));
end loop;
--
-- We can do other things at this point.
-- When done, close the cursor.
--
DBMS_SQL.CLOSE_CURSOR(c);
end;
/
Here is the output when we pass in 3, which queries the ALL_OBJECTS view:
Name Null? Type
----------------------------------------- -------- ----------------------------
OWNER NOT NULL VARCHAR2(128)
OBJECT_NAME NOT NULL VARCHAR2(128)
SUBOBJECT_NAME VARCHAR2(128)
OBJECT_ID NOT NULL NUMBER
DATA_OBJECT_ID NUMBER
OBJECT_TYPE VARCHAR2(23)
CREATED NOT NULL DATE
LAST_DDL_TIME NOT NULL DATE
TIMESTAMP VARCHAR2(19)
STATUS VARCHAR2(7)
TEMPORARY VARCHAR2(1)
GENERATED VARCHAR2(1)
SECONDARY VARCHAR2(1)
NAMESPACE NOT NULL NUMBER
EDITION_NAME VARCHAR2(128)
SHARING VARCHAR2(13)
EDITIONABLE VARCHAR2(1)
ORACLE_MAINTAINED VARCHAR2(1)
Once you are able to see what the columns of the cursor are, you are on your way to solving the problem of creating a dynamic procedure that consumes random cursors and populates arrays.

PL/SQL: How to insert depending on column value

I'm a novice at PL/SQL. I have attempted various approaches to use a Cursor to insert into a temp table depending on whether or not the value already exists in the temp table. I either get too many rows or nothing is inserted.
This is my last pseudocode approach and is the bare essence of what I'm attempt to accomplish:
DB: Oracle 12
Using SQL Developer
Goal: Take duplicate accountno info from table1 and merge / combine into single row in temptable
1. Add initial accountno info if it doesn’t already exists in temptable
2. If accountno exists in temptable add the additional info to accountno row
Suggestions are greatly appreciated.
Pseudocode
Declare
V_cnt number (20);
CURSOR c1 is select * from table1;
C1d c1%rowtype;
BEGIN
--
OPEN C1;
LOOP
FETCH C1 INTO c1d;
EXIT WHEN C1%NOTFOUND;
-- Limit attempts
IF LINE > 5 THEN EXIT; END IF;
select accountno INTO v_cnt from table1 where Exists(select 1 from temptable where accountno <> c1d.accountno);
IF v_cnt is NULL THEN
INSERT INTO temptable (accountno)
values(c1d.accountno);
END IF;
LINE:= LINE + 1;
END LOOP;
CLOSE C1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line ('NO DATA');
END;
If you strictly want to correct your pseudo code, You may try -
Declare
V_cnt number (20);
CURSOR c1 is select * from table1;
C1d c1%rowtype;
BEGIN
--
OPEN C1;
LOOP
FETCH C1 INTO c1d;
EXIT WHEN C1%NOTFOUND;
-- Limit attempts
IF LINE > 5 THEN
EXIT;
END IF;
BEGIN
SELECT accountno
INTO V_cnt
FROM temptable
WHERE accountno = c1d.accountno
AND ROWNUM = 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
V_cnt := NULL;
END;
IF V_cnt is NULL THEN
INSERT INTO temptable (accountno)
values(c1d.accountno);
END IF;
LINE:= LINE + 1;
END LOOP;
CLOSE C1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line ('NO DATA');
END;
I would strongly recommend to use below pseudo code -
Declare
V_cnt number (20);
CURSOR c1 is select * from table1;
C1d c1%rowtype;
BEGIN
--
OPEN C1;
LOOP
FETCH C1 INTO c1d;
EXIT WHEN C1%NOTFOUND;
-- Limit attempts
IF LINE > 5 THEN
EXIT;
END IF;
MERGE INTO temptable
USING table1
ON (accountno = c1d.accountno)
WHEN NOT MATCHED THEN
INSERT (accountno)
values(c1d.accountno);
LINE:= LINE + 1;
END LOOP;
CLOSE C1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line ('NO DATA');
END;

Calcute percentage of every game in

I need to calculate percentage and accumulate it from every game in. I have to parameters p_percentage which is gonna be variable and parameter p_top is also variable and his task is when p_top is satisfied then do something (update or insert) . I get lost here in loop . Is it better to do loop or fetch into v_percentage. And Am I doing it right ?
Thanks
CREATE OR REPLACE FUNCTION "jackpot"
(p_percentage number
,p_top number,
p_player number,
p_party_id number
)
RETURN NUMBER
declare
Cursor c1 is
select p_percentage/game_in *100 into v_percentage from game
where player_id = p_player ;
rw c1%rowtype;
v_top number;
v_percentage number;
BEGIN
open c1;
FOR i IN c1
while v_percentage <= p_top
LOOP
v_percentage:=v_percentage+v_percentage;
end loop;
--update something
close c1;
return v_percentage;
END;
end;
EDIT
create or replace PROCEDURE super_six_jackpot (
i_party_id IN NUMBER,
i_jackpot_limit IN NUMBER,
i_jackpot_perc IN NUMBER,
o_pot_size OUT NUMBER
)
IS
p_username VARCHAR2(100);
p_party_id number;
pot_perc NUMBER;
pot_size NUMBER;
parent_aff NUMBER;
ret_pot_size NUMBER;
pom weak_cur;
BEGIN
SELECT c.party_id, p.aff_id
into p_party_id, parent_aff
FROM casino_users c,pot_by_aff p
WHERE c.parent_id = p.aff_id
AND c.parent_id = :i_party_id;
SELECT total
INTO pot_perc
FROM (
SELECT SUM( i_jackpot_perc/game_in * 100 ) OVER ( ORDER BY ROWNUM ) AS total
FROM game_record
WHERE party_id = p_party_id
ORDER BY total DESC
)
WHERE total <= i_jackpot_limit
AND ROWNUM = 1;
EXECUTE IMMEDIATE 'UPDATE pot_by_aff
SET bingo_jackpot_size = NVL (total, 0)
WHERE aff_id = :parent_aff
RETURNING pot_size
INTO :ret_pot_size'
USING parent_aff,
OUT ret_pot_size;
BEGIN
OPEN pom FOR 'SELECT bingo_jackpot_size
FROM pot_by_aff
WHERE aff_id = :parent_aff'
USING parent_aff;
FETCH pom
INTO ret_pot_size;
CLOSE pom;
END;
o_pot_size := ret_pot_size;
END super_six_jackpot;
Do you simply need to know whether the total percentage for a player reaches or exceeds the top? For that you could use the SUM function in a single SQL statement, no loop. I don't know where game_in comes from, and why you would use p_percentage inside the SELECT, but perhaps this helps:
CREATE OR REPLACE PROCEDURE give_jackpot (p_percentage NUMBER,
p_top NUMBER,
p_player NUMBER,
p_party_id NUMBER)
IS
l_total NUMBER;
BEGIN
SELECT SUM (p_percentage / game_in * 100)
INTO l_total
FROM game
WHERE player_id = p_player;
IF l_total >= p_top
THEN
/* insert or update here */
NULL;
END IF;
END;
Use an analytic function to get the cumulative total in the SQL query.
You do not use DECLARE with specifying a procedure.
Do not use double quoted identifiers if you do not have to.
You cannot use SELECT ... INTO within a cursor.
Like this:
CREATE OR REPLACE FUNCTION jackpot (
p_percentage number,
p_top number,
p_player number,
p_party_id number
) RETURN NUMBER
IS
v_percentage number;
BEGIN
SELECT total
INTO v_percentage
FROM (
SELECT SUM( p_percentage/game_in *100 ) OVER ( ORDER BY ROWNUM ) AS total
FROM game
WHERE player_id = p_player
ORDER BY total DESC
)
WHERE total <= p_top
AND ROWNUM = 1;
--update something
RETURN v_percentage;
END;
/

I have A procedure I need to capture error massage if count = 0 then fetch the record else if count > 0 the capture error massage in error table

PROCEDURE p_get_empdetails(pcu_scheme_info OUT SYS_REFCURSOR)
IS
BEGIN
-- To Select scheme_ref,scheme_cat and quote_ref
OPEN pcu_scheme_info FOR
SELECT emp_ref
,emp_cat
,emp_quote_ref
FROM emp_reset_id e
WHERE estimated_quote_expiry_date <= lpcd_system_date
AND q.quote_ref IN (SELECT DISTINCT qrc.quote_ref
FROM nb005 nb,
emp_reset_id e
WHERE nb.qteref = e.emp_quote_ref
AND nb.pcsstg <> 51);
END p_get_scheme_details;
I need to use below query to fetch record count =0 or count> 0
SELECT COUNT(*)
FROM nb005 nb JOIN emp_reset_id e ON nb.qteref = qrc.emp_quote_ref
WHERE nb.pcsstg=51;
If you want the procedure to finish without any errors when count > 0 and end successfully:
CREATE OR REPLACE PROCEDURE p_get_empdetails(pcu_scheme_info OUT SYS_REFCURSOR)
IS
number v_count := 0;
BEGIN
SELECT COUNT(*) into
FROM nb005 nb JOIN emp_reset_id e ON nb.qteref = qrc.emp_quote_ref
WHERE nb.pcsstg=51;
IF v_count > 0 THEN
--This is where you capture error message in error table.
ELSE
-- To Select scheme_ref,scheme_cat and quote_ref
OPEN pcu_scheme_info FOR
SELECT emp_ref
,emp_cat
,emp_quote_ref
FROM emp_reset_id e
WHERE estimated_quote_expiry_date <= lpcd_system_date
AND q.quote_ref IN (SELECT DISTINCT qrc.quote_ref
FROM nb005 nb,
emp_reset_id e
WHERE nb.qteref = e.emp_quote_ref
AND nb.pcsstg <> 51);
END IF;
END p_get_scheme_details;
If you want the procedure to fail when the count is > 0, but still want to insert records into the error table (my preferred way):
CREATE OR PROCEDURE p_get_empdetails(pcu_scheme_info OUT SYS_REFCURSOR)
IS
v_count number := 0;
v_excep exception;
v_err_msg varchar2(1000) := '';
BEGIN
SELECT COUNT(*) into
FROM nb005 nb JOIN emp_reset_id e ON nb.qteref = qrc.emp_quote_ref
WHERE nb.pcsstg=51;
IF v_count > 0 THEN
raise v_excep;
ELSE
-- To Select scheme_ref,scheme_cat and quote_ref
OPEN pcu_scheme_info FOR
SELECT emp_ref
,emp_cat
,emp_quote_ref
FROM emp_reset_id e
WHERE estimated_quote_expiry_date <= lpcd_system_date
AND q.quote_ref IN (SELECT DISTINCT qrc.quote_ref
FROM nb005 nb,
emp_reset_id e
WHERE nb.qteref = e.emp_quote_ref
AND nb.pcsstg <> 51);
END IF;
EXCEPTION
WHEN v_excep THEN
v_err_msg := 'v_count was greater than 0. '||CHR(10);
--This is where you capture error message in error table.
RAISE_APPLICATION_ERROR(-20555,v_err_msg||'Error: ' || to_char ( SQLCODE ) || ': ' || SQLERRM || '. Backtrace Result: ' || dbms_utility.format_error_backtrace);
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20502,v_err_msg||'Error: ' || to_char ( SQLCODE ) || ': ' || SQLERRM || '. Backtrace Result: ' || dbms_utility.format_error_backtrace);
END p_get_scheme_details;
You can also declare a cursor in the pocedure you are using.
Then use a record to fetch the results from the query that is declared in the cursor.
You could for example even loop through all lines and process then.
For example:
DECLARE
CURSOR CursorName IS
SELECT COUNT(*) ColumnOne
FROM TableA
WHERE Name = 'Me';
RecordNumber CursorName%ROWTYPE;
BEGIN
-- Fetch the records from the cursor.
OPEN CursorName;
LOOP
-- Line by line
FETCH CursorName INTO RecordNumber;
-- Do something with the record.
IF RecordNumber.ColumnOne = 0 THEN
-- Do something with the record.
END IF;
EXIT WHEN CursorName %NOTFOUND;
END LOOP;
CLOSE CursorName;
END;
/
I don't have a database by hand to test my code, but I think this should do.

How to fetch the cursor data in oracle stored procedure

create or replace
PROCEDURE get_new
AS
CUST_ID varchar2(100);
ROUTERNAME_N VARCHAR2(100);
BEGIN
CURSOR c1 IS
SELECT TRAFFIC_CUST_ID,ROUTERNAME INTO CUST_ID,ROUTERNAME_N
FROM INTERFACE_ATTLAS
WHERE rownum > 3;
my_ename INTERFACE_ATTLAS.TRAFFIC_CUST_ID%TYPE;
my_salary INTERFACE_ATTLAS.ROUTERNAME%TYPE;
LOOP
FETCH c1 INTO my_ename;
FETCH c1 INTO my_salary;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(my_ename);
end loop;
end;
I am new to oracle and stored procedure. I am trying to get the rows using cursor fetch, and getting following error:
PLS-00103: Encountered the symbol "C1" when expecting one of the following:
:= . ( # % ;
Rewrite it like this:
create or replace
PROCEDURE get_new
AS
my_ename INTERFACE_ATTLAS.TRAFFIC_CUST_ID%TYPE;
my_salary INTERFACE_ATTLAS.ROUTERNAME%TYPE;
CURSOR c1 IS
SELECT TRAFFIC_CUST_ID,ROUTERNAME
FROM INTERFACE_ATTLAS
WHERE rownum > 3;
BEGIN
open c1;
LOOP
FETCH c1 INTO my_ename, my_salary;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(my_ename);
end loop;
close c1;
end;
Do not forget to open and close cursors. It always will print nothing because of rownum > 3; You wanted to type: rownum < 3;, didn't you?
create or replace
PROCEDURE get_new
AS
CUST_ID varchar2(100);
ROUTERNAME_N VARCHAR2(100);
BEGIN
CURSOR c1 IS
SELECT TRAFFIC_CUST_ID,ROUTERNAME INTO CUST_ID,ROUTERNAME_N
FROM INTERFACE_ATTLAS
WHERE rownum > 3;
my_ename INTERFACE_ATTLAS.TRAFFIC_CUST_ID%TYPE;
my_salary INTERFACE_ATTLAS.ROUTERNAME%TYPE;
LOOP
FETCH c1 INTO my_ename;
FETCH c1 INTO my_salary;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(my_ename);
end loop;
end;
Cursor should be declare in the declaration part. Then have to open in begin-end section. In the declaration section you can not assign value to variable. When you are fetching value you can not select randomly value form cursor,
modified code:
create or replace
PROCEDURE get_new
AS
no mean --CUST_ID varchar2(100);
no means -- ROUTERNAME_N VARCHAR2(100);
CURSOR c1 IS
SELECT deptno,job
FROM emp;
my_ename emp.deptno%TYPE;
my_salary emp.job%TYPE;
BEGIN
open c1;
LOOP
fetch c1 into my_ename,my_salary;
-- FETCH c1 INTO my_ename;
--FETCH c1 INTO my_salary;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(my_ename);
end loop;
end;
execute get_new.
You should probably declare your cursor and your variables my_ename and my_salary in the dedicated section, i.e. before the BEGIN, and then open your cursor:
IS
CUST_ID varchar2(100);
ROUTERNAME_N VARCHAR2(100);
C1 sys_refcursor;
my_ename INTERFACE_ATTLAS.TRAFFIC_CUST_ID%TYPE;
my_salary INTERFACE_ATTLAS.ROUTERNAME%TYPE;
BEGIN
OPEN C1 for
SELECT ...
You would have to declare the Cursor before BEGIN. You would use no INTO clause in the cursor declaration. Then you would have to OPEN the cursor. Then you would FETCH INTO my_ename, my_salary, not one after the other (you fetch rows, not columns). WHERE rownum > 3 returns no rows. As you don't want a first row, you will never get a second, third and fourth either.
And you can use an implicit cursor which is easier to deal with (no need to open, fetch and close explicitely):
BEGIN
FOR rec IN
(
select traffic_cust_id, routername
from interface_attlas
where rownum <= 3
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.traffic_cust_id || ': ' || rec.salary);
END LOOP;
END;

Resources