Calcute percentage of every game in - oracle

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;
/

Related

Writing Oracle stored procedure with Oracle table as Input parameter

How to write Oracle stored procedure with a table (X) as input parameter and that table X is used inside procedure to join with another table Y?
Table X will have thousands of records.
Not looking to pass table name as varchar and then using dynamic SQL (so, this option is out of picture)
From 19.6 you can create a SQL macro. This returns a string with your query fragment.
At parse time the database will do a find/replace of the table parameter with the table you've passed it:
create or replace function f ( tab dbms_tf.table_t )
return varchar2 sql_macro as
begin
return 'select * from tab
join ( select level rn from dual connect by level <= 2 )
on c1 = rn';
end f;
/
create table t1 (
c1 int
);
create table t2 (
c1 int
);
insert into t1 values ( 1 );
insert into t2 values ( 2 );
select * from f ( t1 );
C1 RN
1 1
select * from f ( t2 );
C1 RN
2 2
There's another approach you might find interesting: pass a cursor variable to pipelined table function, invoke it in SQL, allowing you literally pass the contents of the table (select * from...), bulk collect into collection, then join the collection with your other table!
DROP TYPE tickertype FORCE;
DROP TYPE tickertypeset FORCE;
DROP TABLE stocktable;
DROP TABLE tickertable;
CREATE TABLE stocktable
(
ticker VARCHAR2 (20),
trade_date DATE,
open_price NUMBER,
close_price NUMBER
)
/
BEGIN
FOR indx IN 1 .. 100
LOOP
INSERT INTO stocktable
VALUES ('STK' || indx,
SYSDATE,
indx,
indx + 15);
END LOOP;
COMMIT;
END;
/
CREATE TABLE tickertable
(
ticker VARCHAR2 (20),
pricedate DATE,
pricetype VARCHAR2 (1),
price NUMBER
)
/
CREATE TYPE tickertype AS OBJECT
(
ticker VARCHAR2 (20),
pricedate DATE,
pricetype VARCHAR2 (1),
price NUMBER
);
/
BEGIN
FOR indx IN 1 .. 100
LOOP
INSERT INTO tickertable
VALUES ('STK' || indx,
SYSDATE,
'O',
indx);
END LOOP;
COMMIT;
END;
/
CREATE TYPE tickertypeset AS TABLE OF tickertype;
/
CREATE OR REPLACE PACKAGE refcur_pkg
AUTHID DEFINER
IS
TYPE refcur_t IS REF CURSOR
RETURN stocktable%ROWTYPE;
TYPE dataset_tt IS TABLE OF stocktable%ROWTYPE;
END refcur_pkg;
/
CREATE OR REPLACE FUNCTION pipeliner (dataset refcur_pkg.refcur_t)
RETURN tickertypeset
PIPELINED
AUTHID DEFINER
IS
l_row_as_object tickertype
:= tickertype (NULL,
NULL,
NULL,
NULL);
l_dataset refcur_pkg.dataset_tt;
l_count PLS_INTEGER;
BEGIN
FETCH dataset BULK COLLECT INTO l_dataset;
CLOSE dataset;
/* Let's do a join with another table. */
SELECT COUNT (*) into l_count
FROM TABLE (l_dataset) st, tickertable tt
WHERE st.ticker = tt.ticker;
DBMS_OUTPUT.put_line ('Count = ' ||l_count);
l_row_as_object.ticker := 'ABC';
PIPE ROW (l_row_as_object);
RETURN;
END;
/
BEGIN
FOR rec
IN (SELECT * FROM TABLE (pipeliner (CURSOR (SELECT * FROM stocktable))))
LOOP
DBMS_OUTPUT.put_line (rec.ticker);
END LOOP;
END;
/
I see this output:
Count = 100
ABC
Create a table type in the SQL scope:
CREATE TYPE string_list AS TABLE OF VARCHAR2(5);
Then use that as the parameter for your stored procedure and join it to another table using a Table Collection Expression:
CREATE PROCEDURE test_proc(
p_list IN string_list
)
IS
v_cursor SYS_REFCURSOR;
v_string VARCHAR2(10);
BEGIN
OPEN v_cursor FOR
SELECT d.*
FROM DUAL d
INNER JOIN TABLE( p_list ) t
ON ( d.DUMMY = t.COLUMN_VALUE );
-- do something with the cursor.
LOOP
FETCH v_cursor into v_string;
EXIT WHEN v_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE( v_string );
END LOOP;
END;
/
Then you can call it:
BEGIN
test_proc( string_list( 'X', 'Y', 'Z' ) ) ;
END;
/
and it outputs:
X
db<>fiddle here

creating cursor for counting

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;
/

Updating column is not working in Oracle PLSQL

I have a Stored procedure in PLSQL which Inserts and Updates records on the basis of some condition.
Now here the issue is. While Inserting the record for the first time, it inserts records properly as required but
while updating it doesn't updates the record of the table.
Below is SP
PROCEDURE INSERT_INTO_VSAT_MST_DATA
(
P_SAPID IN NVARCHAR2,
P_CIRCLE IN NVARCHAR2,
P_CANDIDATEID IN NVARCHAR2,
P_SITEID IN NVARCHAR2,
P_PRIORITYID IN NVARCHAR2,
P_SITENAME IN NVARCHAR2,
P_LATITUDE IN NVARCHAR2,
P_LONGITUDE IN NVARCHAR2,
P_CONTACT_DETAILS IN CLOB,
P_SITETYPE IN NVARCHAR2,
P_SITE_PLOT_DIMENSION IN NUMBER,
P_TECHNOLOGY IN NVARCHAR2
)
AS
V_COUNT NUMBER:=0;
V_PANAROMICIMG_COUNT NUMBER:=0;
V_SATELLITEIMG_COUNT NUMBER:=0;
V_SITEPLOTIMG_COUNT NUMBER:=0;
V_VSAT_DETAIL_ID NUMBER:=0;
BEGIN
SELECT COUNT(VSAT_DETAIL_ID) INTO V_COUNT FROM TBL_VSAT_MST_DETAIL WHERE SAP_ID = P_SAPID AND CANDIDATE_ID = P_CANDIDATEID;
IF V_COUNT > 0 THEN
SELECT VSAT_DETAIL_ID INTO TBL_INSERT FROM TBL_VSAT_MST_DETAIL WHERE SAP_ID = P_SAPID AND CANDIDATE_ID = P_CANDIDATEID;
UPDATE TBL_VSAT_MST_DETAIL SET
CIRCLE = P_CIRCLE,
CONTACT_DETAILS = P_CONTACT_DETAILS,
SITE_TYPE = P_SITETYPE,
SITE_DETAILS_DIMENSION = P_SITE_PLOT_DIMENSION,
SITE_DETAILS_TECHNOLOGY = P_TECHNOLOGY
WHERE VSAT_DETAIL_ID = V_VSAT_DETAIL_ID
RETURNING VSAT_DETAIL_ID INTO TBL_INSERT;
ELSE
INSERT INTO TBL_VSAT_MST_DETAIL
(
SAP_ID,
CIRCLE,
CANDIDATE_ID,
SITE_ID,
PRIORITY,
SITE_NAME,
LATITUDE,
LONGITUDE,
CONTACT_DETAILS,
SITE_TYPE,
SITE_DETAILS_DIMENSION,
SITE_DETAILS_TECHNOLOGY
VALUES
(
P_SAPID,
P_CIRCLE,
P_CANDIDATEID,
P_SITEID,
P_PRIORITYID,
P_SITENAME,
P_LATITUDE,
P_LONGITUDE,
P_CONTACT_DETAILS,
P_SITETYPE,
P_SITE_PLOT_DIMENSION,
P_TECHNOLOGY
) RETURNING VSAT_DETAIL_ID INTO TBL_INSERT;
END IF;
IF TBL_INSERT > 0 THEN
BEGIN
SELECT COUNT(*) INTO V_PANAROMICIMG_COUNT FROM TBL_VSAT_IMAGE_DETAIL WHERE IMG_TYPE = 'Panaromic' AND IMG_ID = TBL_INSERT;
SELECT COUNT(*) INTO V_SATELLITEIMG_COUNT FROM TBL_VSAT_IMAGE_DETAIL WHERE IMG_TYPE = 'Satellite' AND IMG_ID = TBL_INSERT;
SELECT COUNT(*) INTO V_SITEPLOTIMG_COUNT FROM TBL_VSAT_IMAGE_DETAIL WHERE IMG_TYPE = 'SitePlot' AND IMG_ID = TBL_INSERT;
IF V_PANAROMICIMG_COUNT > 0 THEN
BEGIN
DELETE FROM TBL_VSAT_IMAGE_DETAIL WHERE IMG_TYPE = 'Panaromic' AND IMG_ID = TBL_INSERT;
END;
END IF;
IF V_SATELLITEIMG_COUNT > 0 THEN
BEGIN
DELETE FROM TBL_VSAT_IMAGE_DETAIL WHERE IMG_TYPE = 'Satellite' AND IMG_ID = TBL_INSERT;
END;
END IF;
IF V_SITEPLOTIMG_COUNT > 0 THEN
BEGIN
DELETE FROM TBL_VSAT_IMAGE_DETAIL WHERE IMG_TYPE = 'SitePlot' AND IMG_ID = TBL_INSERT;
END;
END IF;
FOR PMULTIFIELDS IN (SELECT REGEXP_SUBSTR(P_PANORAMIC_IMAGES,'[^,]+', 1, LEVEL) AS IMAGES FROM DUAL
CONNECT BY REGEXP_SUBSTR(P_PANORAMIC_IMAGES, '[^,]+', 1, LEVEL) IS NOT NULL
)
LOOP
INSERT INTO TBL_VSAT_IMAGE_DETAIL
(
IMG_ID,
IMG_NAME,
IMG_TYPE,
IMG_UPLOADED_DATE,
UPLOADED_BY
)
VALUES
(
TBL_INSERT,
PMULTIFIELDS.IMAGES,
'Panaromic',
SYSDATE,
P_CREATEDBY
);
END LOOP;
FOR PSATELLITEIMG IN (SELECT REGEXP_SUBSTR(P_SATELLITE_IMAGES,'[^,]+', 1, LEVEL) AS IMAGES FROM DUAL
CONNECT BY REGEXP_SUBSTR(P_SATELLITE_IMAGES, '[^,]+', 1, LEVEL) IS NOT NULL
)
LOOP
INSERT INTO TBL_VSAT_IMAGE_DETAIL
(
IMG_ID,
IMG_NAME,
IMG_TYPE,
IMG_UPLOADED_DATE,
UPLOADED_BY
)
VALUES
(
TBL_INSERT,
PSATELLITEIMG.IMAGES,
'Satellite',
SYSDATE,
P_CREATEDBY
);
END LOOP;
IF P_SITEPLOT_IMAGES IS NOT NULL THEN
BEGIN
INSERT INTO TBL_VSAT_IMAGE_DETAIL
(
IMG_ID,
IMG_NAME,
IMG_TYPE,
IMG_UPLOADED_DATE,
UPLOADED_BY
)
VALUES
(
TBL_INSERT,
P_SITEPLOT_IMAGES,
'SitePlot',
SYSDATE,
P_CREATEDBY
);
END;
END IF;
END;
END IF;
COMMIT;
EXCEPTION WHEN OTHERS THEN
ROLLBACK;
NOTE
While updating the record my TBL_INSERT returns as NULL
Expanding on what #user7294900 pointed you towards... in the declare section you have:
V_VSAT_DETAIL_ID NUMBER:=0;
then if v_count > 0 you do:
SELECT VSAT_DETAIL_ID INTO TBL_INSERT FROM TBL_VSAT_MST_DETAIL WHERE SAP_ID = P_SAPID AND CANDIDATE_ID = P_CANDIDATEID;
UPDATE TBL_VSAT_MST_DETAIL SET
CIRCLE = P_CIRCLE,
CONTACT_DETAILS = P_CONTACT_DETAILS,
SITE_TYPE = P_SITETYPE,
SITE_DETAILS_DIMENSION = P_SITE_PLOT_DIMENSION,
SITE_DETAILS_TECHNOLOGY = P_TECHNOLOGY
WHERE VSAT_DETAIL_ID = V_VSAT_DETAIL_ID
RETURNING VSAT_DETAIL_ID INTO TBL_INSERT;
The select is setting TBL_INSERT to the ID value from your table. But when you do the update your filter is using V_VSAT_DETAIL_ID, which is still set to its initial value of zero.
You probably meant to do:
SELECT VSAT_DETAIL_ID INTO V_VSAT_DETAIL_ID FROM TBL_VSAT_MST_DETAIL WHERE SAP_ID = P_SAPID AND CANDIDATE_ID = P_CANDIDATEID;
although you could still with your current select, and use that in the update too (making the returning into a bit redundant.
Be aware though that if v_count is not exactly 1, i.e. you have more than one row matching the P_SAPID and P_CANDIDATEID values, the select will get a too-many-rows exception. You won't see that because you are silently squashing any errors you get at run time.
It's usually not a good idea to commit or rollback inside a procedure anyway; it should be up to the caller to decide what to do, as this could be one of a series of statements and calls that you really want to treat as an atomic transaction. (You may be interested in savepoints.)
If you really, really want to rollback on exception within the procedure, you should at least re-raise the exception so the caller knows there was a problem:
EXCEPTION WHEN OTHERS THEN
ROLLBACK;
RAISE;
END INSERT_INTO_VSAT_MST_DATA;
but I would avoid when others if you can.
You could also combine a few steps by getting the ID at the start (again kind of assuming there is at most one matching row), and dropping the separate count and v_count variable:
SELECT MAX(VSAT_DETAIL_ID) INTO V_VSAT_DETAIL_ID
FROM TBL_VSAT_MST_DETAIL
WHERE SAP_ID = P_SAPID AND CANDIDATE_ID = P_CANDIDATEID;
IF V_VSAT_DETAIL_ID IS NOT NULL THEN
UPDATE TBL_VSAT_MST_DETAIL SET
CIRCLE = P_CIRCLE,
CONTACT_DETAILS = P_CONTACT_DETAILS,
SITE_TYPE = P_SITETYPE,
SITE_DETAILS_DIMENSION = P_SITE_PLOT_DIMENSION,
SITE_DETAILS_TECHNOLOGY = P_TECHNOLOGY
WHERE VSAT_DETAIL_ID = V_VSAT_DETAIL_ID
RETURNING VSAT_DETAIL_ID INTO TBL_INSERT;
ELSE
...
And I'm not sure why you're doing counts before your deletes later on, and it looks like all your tbl_insert references could/should be v_vast_detail_id - there doesn't seem an obvious reason to have two variables for that. Passing in a comma-delimited string that you then have to tokenize is also a bit painful - you should consider passing in a collection of values instead, if whatever calls this can manage that.
As also pointed out, you could use merge instead of the separate insert/update logic.
You don't assign value to V_VSAT_DETAIL_ID which is used in your update as a key.
You should use merge for this kind of operations

compare xtable count with table in oracle procedure

(I have several tables and for each table I have corresponding xtable.
what I am trying to do is create oracle procedure that will compare counts between xtable and table,
if (xtable >= table)
do something...
type namesarray IS VARRAY(5) OF VARCHAR2(10);
tbl_names namesarray;
xtbl_names namesarray;
total integer;
BEGIN
tbl_names := namesarray('tbl1', 'tbl2', 'tbl3', 'tbl4');
xtbl_names := namesarray('xtbl1', 'xtbl2', 'xtbl3', 'xtbl4');
total := tbl_names.count;
FOR i in (
SELECT COUNT(*) as TBL_COUNTS FROM tbl_names(i);
SELECT COUNT(*) as XTBL_COUNTS FROM xtbl_names(i)
)
LOOP
IF(i.XTBL_COUNTS >= i.TBL_COUNTS )
THEN
-- print to console
END IF;
END LOOP;
END;
This is all I got so far.
Can someone help?
How about this?
if (xtable >= table)
do something...
type namesarray IS VARRAY(5) OF VARCHAR2(10);
tbl_names namesarray;
xtbl_names namesarray;
total integer;
v_count_1 integer;
v_count_2 integer;
BEGIN
tbl_names := namesarray('tbl1', 'tbl2', 'tbl3', 'tbl4');
xtbl_names := namesarray('xtbl1', 'xtbl2', 'xtbl3', 'xtbl4');
total := tbl_names.count;
FOR i in (
select
t1.table_name as tab_1_name, t2.table_name as tab_2_name
from ( select table_name from user_tables where table_name in (...) ) t1
,( select 'x'||table_name as table_name from user_tables where table_name in (...) ) t2
where 'x'||t1.table_name = t2.table_name
)
LOOP
execute immediate 'select count(*) from '||i.tab_1_name into v_count_1;
execute immediate 'select count(*) from '||i.tab_2_name into v_count_2;
IF( v_count_1 >= v_count_2 )
THEN
-- print to console
END IF;
END LOOP;
END;

Convert array of records to refcursor

The question is how to return the l_array as refcursor,
Since the interface i am using can handle cursor easily rather than an array of record.
Plz help
create or replace package sample
TYPE r_type is record( code number; description varchar2(50));
TYPE tr_type IS TABLE OF r_type; l_rarray tr_type ; ind number:=0;
PROCEDURE getdata() IS
CURSOR cur IS
SELECT empid, empname, place, location FROM emp;
TYPE epmid_aat IS TABLE OF emp.empid%TYPE INDEX BY BINARY_INTEGER;
l_empid empid_aat;
BEGIN
k := 1;
FOR j IN (SELECT DISTINCT empid FROM emp)
LOOP
l_empid(k) := j.empid;
k := k + 1;
END LOOP;
FOR i IN cur
LOOP
FOR k IN l_empid.first .. l_empid.last
LOOP
IF l_empid(k) = i.empid THEN
procedure2(i.emp_id);
END IF;
END LOOP;
END LOOP;
END getdata();
PROCEDURE procedure2
(
empid_in IN NUMBER,
description_in IN VARCHAR2(20)
) IS
BEGIN
lrec.code := empid_in;
lrec.description := description_in;
l_rarray(ind) := lrec;
ind := ind + 1;
END procedure2;
end;
I think it should be like this :
OPEN YourRefCursor
FOR SELECT * FROM TABLE (Cast(l_rarray AS tr_type));
Something like this.
TYPE r_type is record ( code number;
description varchar2(50)
);
TYPE tr_type IS TABLE OF r_type;
l_rarray tr_type ;
SELECT r_type(empid, empname)
BULK COLLECT INTO l_rarray
FROM emp;
OPEN YourRefCursor
SELECT *
FROM TABLE (Cast(l_rarray AS r_type));

Resources