Oracle cannot perform dml operations cant be called inside query - oracle

CREATE OR REPLACE FUNCTION cash_out_ticket(party_id IN softdev.casino_users.party_id%TYPE ,session_id IN softdev.bus_session.session_id%TYPE
) RETURN NUMBER AS ret_val NUMBER;
P_EXCHANGE_BET_CREDITS softdev.COUNTRY.EXCHANGE_BET_CREDITS%type;
P_EXCHANGE_VALUE softdev.COUNTRY.EXCHANGE_VALUE%type;
p_reserved_funds softdev.casino_users.credits%type;
p_session_id softdev.bus_session.session_id%type;
p_session_close softdev.bus_session.session_close%type;
ticket_closed exception;
CURSOR cur_tkt_sess (party_id IN softdev.casino_users.party_id%TYPE,session_id IN softdev.bus_session.session_id%TYPE)
IS
SELECT bs.session_id
,tii.status
,ROW_NUMBER() OVER (PARTITION BY bs.session_id ORDER BY status ASC) rn
,NVL(TO_CHAR(bs.started, 'DD.MM.YYYY HH24:MI'), 'Live') started
,bs.bet bet
-- ,bs.player_win * P_EXCHANGE_BET_CREDITS / P_EXCHANGE_VALUE AS possible_win
-- ,bs.house_win AS odds
,tii.time_p
,tii.live_prematch
,cash_out(bet) cash_out
FROM bus_session bs
,ticket_items tii
WHERE bs.session_id = tii.bus_session_session_id
AND bs.session_type = 'TICKET SESSION'
AND bs.party_id = cur_tkt_sess.party_id
AND bs.session_id = cur_tkt_sess.session_id
AND NVL(bs.session_close, 'N') = 'N';
rec_tkt_sess cur_tkt_sess%ROWTYPE;
BEGIN
CHAGE_CREDITS (party_id, P_EXCHANGE_BET_CREDITS, P_EXCHANGE_VALUE);
OPEN cur_tkt_sess(cash_out_ticket.party_id, cash_out_ticket.session_id);
FETCH cur_tkt_sess
INTO rec_tkt_sess;
IF(cur_tkt_sess%FOUND) THEN
IF(
(TO_DATE(rec_tkt_sess.started,'DD.MM.YYYY HH24:MI:SS') +1 <=SYSDATE) -- je li tiket stariji od 24h
OR
(rec_tkt_sess.live_prematch != '0') --je li live (1 live , 0 not live)
OR
(rec_tkt_sess.time_p <SYSDATE) -- je li utakmice počela
)
THEN
ret_val := 0;
ELSE
ret_val := rec_tkt_sess.cash_out* P_EXCHANGE_BET_CREDITS / P_EXCHANGE_VALUE;
play_beting.end_of_ticket(cash_out_ticket.session_id ,rec_tkt_sess.cash_out* P_EXCHANGE_BET_CREDITS / P_EXCHANGE_VALUE ,cash_out_ticket.party_id );
--this procedure do updates , does not return anything. And it need to be run when conditions are fulfilled.
END IF;
ELSE
ret_val := -1;
END IF;
RETURN(ret_val);
END cash_out_ticket;
I have this function cash_out_ticket which goes through cursor and then cursors items runs through IF statement if conditions are fulfilled it need to return cash_out and run procedure end_of_ticket.
But when i call cash_out_ticket from dual i get error that i cannot perform dml operation inside query .
In my case dml operations are inside of end_of_ticket. It doesnt return anything just do some updates from IN parameters.
Is there way to call end_of_ticket when conditions from if are right?

As the error says, you can't call your function from a query because of the DML (insert/update/delete) in the procedure it calls. So you can't do:
select cash_out_ticket(42, 123) from dual;
ORA-14551: cannot perform a DML operation inside a query
You can call it from a PL/SQL context, e.g. in an anonymous block:
declare
ret_val number;
begin
ret_val := cash_out_ticket(party_id => 42 ,session_id => 123);
-- do something with ret_val
end;
/
Depending on where and how you had planned to call it you could use a bind variable to retrieve the return value.

Related

how to cancel Pipe row?

I have a pipeline function. If I catch an exception, I want to return no lines. Even if the exception occurs after the first utilisation of pipe row.
CREATE TYPE a_r IS OBJECT (a1 VARCHAR (1), a2 VARCHAR (1));
CREATE TYPE a_t IS TABLE OF a_r;
CREATE or replace FUNCTION get_a
RETURN a_t
PIPELINED
IS
BEGIN
FOR c IN (SELECT '1' a , '2' b FROM DUAL)
LOOP
PIPE ROW (a_r(c.a,c.b));
END LOOP;
FOR a
IN (SELECT 'a2' a,
'b' b
FROM DUAL)
LOOP
PIPE ROW (a_r(a.a,a.b));
END LOOP;
exception
WHEN VALUE_ERROR
THEN
DBMS_OUTPUT.put_line ('VALUE_ERROR');
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('other');
END;
select * from table(get_a())
In this example, Before the error ('ab' is not varchar2(1)) occurs, the first line is already piped. But I want nothing to be returned. Is there a way to write cancel what was piped in the exception block?
code
No because the client may already have consumed the row.
You could code your function so that it fills a local collection with data and only at the end decides whether to iterate through the collection to actually return the rows. That will require more memory since you're materializing the entire result set before returning it and defeats some of the performance benefits of using pipelined table functions.
Something like this would appear to do what you want
CREATE or replace FUNCTION get_a
RETURN a_t
PIPELINED
IS
l_a_t a_t := new a_t();
BEGIN
FOR c IN (SELECT '1' a , '2' b FROM DUAL)
LOOP
l_a_t.extend;
l_a_t( l_a_t.count ) := a_r(c.a,c.b);
END LOOP;
FOR a
IN (SELECT 'a2' a,
'b' b
FROM DUAL)
LOOP
l_a_t.extend;
l_a_t( l_a_t.count ) := a_r(a.a,a.b);
END LOOP;
for i in 1 .. l_a_t.count
loop
pipe row( l_a_t(i) );
end loop;
exception
WHEN VALUE_ERROR
THEN
DBMS_OUTPUT.put_line ('VALUE_ERROR');
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('other');
END;
/
See this dbfiddle

PL/SQL Unable to get return value of a function when called through trigger - Oracle

I am calling a function in oracle in an after update trigger. The Function is returning a value that is equated to perform a select and an insert operation.
The issue is when I am calling this function in the trigger it is getting terminated, that is it is not performing the corresponding insert operation. But the function is working fine when I execute it by itself. Also, if the trigger is run by removing the condition which is returned by the function, it is getting executed as expected.
Function:
CREATE OR REPLACE FUNCTION VERIFY_FINAL
(case_id IN number)
RETURN varchar2
IS
is_marked_final varchar2(4);
loop_count number(2);
cursor c1 is
SELECT sub_case_status from
cdm_master_sub_case
where master_id = (case_id);
BEGIN
is_marked_final := 'Y';
loop_count := 0;
FOR rec in c1
LOOP
IF (rec.sub_case_status = '1') THEN
is_marked_final := 'Y';
ELSIF (rec.sub_case_status = '2') THEN
is_marked_final := 'Y';
ELSE
loop_count := loop_count + 1;
END if;
END LOOP;
IF (loop_count > 0) THEN
is_marked_final := 'N';
END if;
RETURN is_marked_final;
END;
Trigger:
CREATE OR REPLACE TRIGGER CDM_MASTER_SUB_CASE_TRIGGER
AFTER UPDATE
on CDM_MASTER_SUB_CASE
FOR EACH ROW
DECLARE
check_var varchar2(4);
unique_id varchar2(100);
transaction_id number(10);
BEGIN
transaction_id := :new.MASTER_ID;
check_var := VERIFY_FINAL(transaction_id);
IF (check_var = 'Y') THEN
select UNIQUE_CUST_ID
INTO unique_id
from ASM355.cdm_matches
where MASTER_ID = :new.MASTER_ID
and rownum = 1;
INSERT INTO tracking_final_cases (MASTER_ID,unique_cust)
values (:new.master_id,unique_id);
END if;
END;
I would appreciate it if anyone can point me in the right direction.
1.) As tmrozek points out a return of 'N' will not do the associated insert. I might suggest having an ELSE to that IF that does something to indicate if that is what is happening.
2.) I would also point out that your SELECT INTO, if it does not find a corresponding value, would cause issues. You might want to do something to ensure that this trigger is failsafe, or have you considered what you want the code to do if that situation occurs? (Error out? Insert a null unique_id?)
3.) If you are looking at the results from a different session, bear in mind that the inserted tracking_final_cases will not be visible until you commit your changes in the session that called the trigger.
I don't know your table data but it is possible to your function to return 'N' so it wouldn't meet your trigger condition (check_var = 'Y').
If you run command like that:
update CDM_MASTER_SUB_CASE
set sub_case_status = 3;
you will probably get your problem.
Thanks guys for the time, it got resolved. I was querying a select statement in the function body over a table on which the corresponding trigger was created.

ORA-01007 "variable not in select list" from dbms_sql.column_value call

I am trying to use dynamic SQL to sample all the data in a schema with a pattern:
DECLARE
xsql varchar2(5000);
c NUMBER;
d NUMBER;
col_cnt INTEGER;
f BOOLEAN;
rec_tab DBMS_SQL.DESC_TAB;
col_num NUMBER;
varvar varchar2(500);
PROCEDURE print_rec(rec in DBMS_SQL.DESC_REC) IS
BEGIN
DBMS_OUTPUT.ENABLE(1000000);
DBMS_OUTPUT.NEW_LINE;
DBMS_OUTPUT.PUT_LINE('col_type = '
|| rec.col_type);
DBMS_OUTPUT.PUT_LINE('col_maxlen = '
|| rec.col_max_len);
DBMS_OUTPUT.PUT_LINE('col_name = '
|| rec.col_name);
DBMS_OUTPUT.PUT_LINE('col_name_len = '
|| rec.col_name_len);
DBMS_OUTPUT.PUT_LINE('col_schema_name = '
|| rec.col_schema_name);
DBMS_OUTPUT.PUT_LINE('col_schema_name_len = '
|| rec.col_schema_name_len);
DBMS_OUTPUT.PUT_LINE('col_precision = '
|| rec.col_precision);
DBMS_OUTPUT.PUT_LINE('col_scale = '
|| rec.col_scale);
DBMS_OUTPUT.PUT('col_null_ok = ');
IF (rec.col_null_ok) THEN
DBMS_OUTPUT.PUT_LINE('true');
ELSE
DBMS_OUTPUT.PUT_LINE('false');
END IF;
END;
BEGIN
c := DBMS_SQL.OPEN_CURSOR;
xsql:='
WITH got_r_num AS
(
SELECT e.* -- or whatever columns you want
, ROW_NUMBER () OVER (ORDER BY dbms_random.value) AS r_num
FROM dba_tab_columns e
)
SELECT * -- or list all columns except r_num
FROM got_r_num
WHERE r_num <= 10';
DBMS_SQL.PARSE(c, xsql, DBMS_SQL.NATIVE);
d := DBMS_SQL.EXECUTE(c);
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
NULL;
-- get column values of the row
DBMS_SQL.COLUMN_VALUE(c, 2, varvar);
--dbms_output.put_line('varvar=');
--DBMS_SQL.COLUMN_VALUE(source_cursor, 2, name_var);
--DBMS_SQL.COLUMN_VALUE(source_cursor, 3, birthdate_var);
-- Bind the row into the cursor that inserts into the destination table. You
-- could alter this example to require the use of dynamic SQL by inserting an
-- if condition before the bind.
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':id_bind', id_var);
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':name_bind', name_var);
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':birthdate_bind',
--birthdate_var);
--ignore := DBMS_SQL.EXECUTE(destination_cursor);
--ELSE
-- No more rows to copy:
--EXIT;
END IF;
END LOOP;
--EXIT WHEN d != 10;
--END LOOP;
col_num := rec_tab.first;
IF (col_num IS NOT NULL) THEN
LOOP
print_rec(rec_tab(col_num));
col_num := rec_tab.next(col_num);
EXIT WHEN (col_num IS NULL);
END LOOP;
END IF;
DBMS_SQL.CLOSE_CURSOR(c);
END;
/
When I run that it gives me this error from the line with the dbms_sql.column_value call:
ORA-01007: variable not in select list
If I comment out that dbms_sql.column_value call it still errors but now with:
ORA-01002: fetch out of sequence
What am I doing wrong?
You have two problems in the code you posted. Firstly you have skipped part of the execution flow because you haven't called the DEFINE_COLUMN procedure. That is what is causing the ORA-01007 error, as the dynamic SQL processing hasn't been told about the select list columns via that call. For your current code you only need to define column 2, but assuming you will actually want to refer to the others you can define them in a loop. To treat them all as string for display you could do:
...
DBMS_SQL.PARSE(c, xsql, DBMS_SQL.NATIVE);
d := DBMS_SQL.EXECUTE(c);
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
FOR i IN 1..col_cnt
LOOP
-- dbms_output.put_line('col_name is ' || rec_tab(i).col_name);
DBMS_SQL.DEFINE_COLUMN(c, i, varvar, 500);
END LOOP;
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
...
If you want to do anything that needs to treat the variables as the right types you could have a local variable of each type and use the data type from the rec_tab information you already have from describe_columns to use the appropriately typed variable for each column.
The second problem, which you were hitting when you commented the column_value call, is still there once that definbe issue has been fixed. Your loop doesn't ever exit, so after you fetch the last row from the cursor you do a further invalid fetch, which throws ORA-01002. You have the code to avoid that already but it's commented out:
...
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
-- get column values of the row
DBMS_SQL.COLUMN_VALUE(c, 2, varvar);
...
ELSE
-- No more rows to copy:
EXIT;
END IF;
END LOOP;
...
With those two changes your code runs, and dumps the view structure:
PL/SQL procedure successfully completed.
col_type = 1
col_maxlen = 30
col_name = OWNER
col_name_len = 5
col_schema_name =
col_schema_name_len = 0
col_precision = 0
col_scale = 0
col_null_ok = false
col_type = 1
col_maxlen = 30
col_name = TABLE_NAME
...
To those who find this question when accessing Oracle through ODP.NET, as I did:
We started getting this error whenever we would add column to an existing table in our application. I'm not sure what all the conditions were to make it fail, but ours were:
Run a SELECT * FROM "table".
Include a ROWNUM restriction in the WHERE clause (WHERE ROWNUM < 10).
Run that through the ODP.NET dataReader.GetSchemaTable() call.
Running unrestricted queries or running queries directly on Oracle SQL Developer did not seem to cause the error.
I've hit some pretty weird stuff in the past with Oracle connection pooling, so I eventually thought that could be the problem. The solution was to restart the web service to force all the connections to be fully dropped and recreated.
The theory is that the ODP.NET connection from the connection pool still had no idea the column existed on the table, but the column was returned by the database.

PL/SQL If statement, cursors, and a record set. How to use an IF statement after I use the Cursor?

I am wondering if I am going about this the right way.
My main issue that compiler gives is for this line
IF SELECT 1 FROM works WHERE an_employee.employee_name IN works.manager_name THEN
Error(17,8): PLS-00103: Encountered the symbol "SELECT" when expecting one of the following: ( - + case mod new not null continue avg count current exists max min prior sql stddev sum variance execute forall merge time timestamp interval date pipe
What I am trying to do is use a cursor that detects the first condition if they work in a company that is in that city. In this example in my table it will give 2 companies, and about 7 or 8 employees. Then I put that into my variable an_employee. What I am trying to do is use that employee then to see if they are a manager from my manages table which has multiple rows/tuples. Not everyone is a manager. How can I see if my employee_name is in the list of manager_name?
Do I declare another cursor for a manager and then do a nested loop?
Can I use my existing cursor and do a select query to get the list of managers from the table manages.manager_name? If I do that, how can I use it in the IF statement as my condition?
-- Give all employees that work in a company located in city X
-- a Y percent raise if they are managers and
-- a Z percent raise if they are not a manager
-- X, Y, and Z will be the three parameters for the stored procedure.
-- Build / compile a stored procedure
CREATE OR REPLACE PROCEDURE give_raises(X company.city%TYPE, Y NUMBER, Z NUMBER) IS
an_employee works.employee_name%TYPE;
-- cursor declaration
cursor Cursor1 IS
select works.employee_name
from works
where works.company_name IN (select company_name from company where city = 'London');
BEGIN
SELECT manager_name INTO managers FROM manages;
OPEN Cursor1;
LOOP
FETCH Cursor1 INTO an_employee;
EXIT WHEN Cursor1%NOTFOUND;
-- is a manager give Y percent raise
IF SELECT 1 FROM works WHERE an_employee.employee_name IN works.manager_name THEN
update works
set works.salary = works.salary + (works.salary * Y)
where works.employee_name = an_employee.employee_name;
ELSE -- is not a manager give Z percent raise
update works
set works.salary = works.salary + (works.salary * Z)
where works.employee_name = an_employee.employee_name;
END IF;
END LOOP;
CLOSE Cursor1;
END;
Also you should know. I am using Oracle, Oracle Sql Developer and the IDE.
If works on a logical condition and does not support Select statements in the condition.
You need to rework your code like this
SELECT count(*)
INTO v_value
FROM works
WHERE an_employee.employee_name = works.manager_name;
IF v_value = 1 THEN
--do some stuff
ELSE
--do some other stuff
END IF;
If i understand your problem correctly you are trying to identify whether an employee is manager or not based on thatsalary is computed. Hope below snippet helps.
CREATE OR REPLACE PROCEDURE give_raises(
X company.city%TYPE,
Y NUMBER,
Z NUMBER)
AS
managers PLS_INTEGER;
BEGIN
FOR an_employee IN
(SELECT works.employee_name
FROM works
WHERE works.company_name IN
(SELECT company_name FROM company WHERE city = 'London'
)
)
LOOP
SELECT COUNT(1)
INTO managers
FROM manages m
WHERE m.manager_name = an_employee.employee_name;
-- is a manager give Y percent raise
IF managers <> 0 THEN
UPDATE works
SET works.salary = works.salary + (works.salary * Y)
WHERE works.employee_name = an_employee.employee_name;
ELSE -- is not a manager give Z percent raise
UPDATE works
SET works.salary = works.salary + (works.salary * Z)
WHERE works.employee_name = an_employee.employee_name;
END IF;
END LOOP;
END;
Your problem is essentially the same than e.g. in a question posted yesterday. PL/SQL if statement expects a boolean expression not a query result set.
A good practice is to encapsulate the query to a function returning a suitable value.
Example:
SQL> !cat so53.sql
declare
function is_foo_1(p_foo in varchar2) return boolean is
v_exists number;
begin
select count(*)
into v_exists
from dual
where dummy = p_foo
and rownum = 1
;
return
case v_exists
when 1 then true
else false
end;
end;
function is_foo_2(p_foo in varchar2) return number is
v_exists number;
begin
select count(*)
into v_exists
from dual
where dummy = p_foo
and rownum = 1
;
return v_exists;
end;
begin
-- is_foo_1 returns a boolean value than is a valid boolean expression
if is_foo_1('X')
then
dbms_output.put_line('1:X');
end if;
if not is_foo_1('Y')
then
dbms_output.put_line('1:not Y');
end if;
-- is_foo_2 returns a number that is not a valid boolean expression (PL/SQL
-- doesn't have implicit type conversions) so one have to use an operator
-- (in this example `=`-operator) to construct an explicit boolean
-- expression
if is_foo_2('X') = 1
then
dbms_output.put_line('2:X');
end if;
if is_foo_2('Y') = 0
then
dbms_output.put_line('2:not Y');
end if;
end;
/
Example run:
SQL> #so53.sql
1:X
1:not Y
2:X
2:not Y
PL/SQL procedure successfully completed.
SQL>

Storing an entire column in a variable inside a stored procedure

I have a complex stored procedure inside a package. Inside this SP, I need to query a table to get all the data pertaining one column and then use this to check some other condition inside an "IF" statement.
Here is what I am doing:
--declare a variable to store the holidays
l_holidays MySchema. HolidayTable.DateColumn%TYPE
-- populate this variable
Select a.DateColumn into l_holidays
from MySchema. HolidayTable a;
-- using this variable inside an "IF" statement
IF (current_Date IN l_holidays)
THEN
-- do something
ELSE
-- do something
END IF;
Every time I run this, I get the following error
ORA-01422: exact fetch returns more than requested number of rows
I know this is because I am trying to populate the entire column using the "INTO" clause. But I don't know any other way of doing it.
Create a collection and use BULK COLLECT INTO:
CREATE PROCEDURE my_proc (
current_date IN MySchema.HolidayTable.DateColumn%TYPE
)
AS
TYPE date_tab IS TABLE OF MySchema.HolidayTable.DateColumn%TYPE;
l_holidays date_tab;
BEGIN
SELECT DateColumn
BULK COLLECT INTO l_holidays
FROM MySchema.HolidayTable;
IF (current_Date MEMBER OF l_holidays)
THEN
NULL; -- do something
ELSE
NULL; -- do something
END IF;
END;
Otherwise you can just test in the select:
CREATE PROCEDURE my_proc (
current_date IN MySchema.HolidayTable.DateColumn%TYPE
)
AS
has_date NUMBER(1,0);
BEGIN
SELECT CASE WHEN EXISTS ( SELECT 'X'
FROM MySchema.HolidayTable
WHERE DateColumn = Current_Date )
THEN 1
ELSE 0
END
INTO has_date
FROM DUAL;
IF has_date = 1
THEN
NULL; -- do something
ELSE
NULL; -- do something
END IF;
END;
Hello similarly you can use this query to fulfill your requirements
SET serveroutput ON;
SET sqlbl ON;
DECLARE
type l_holiday
IS
TABLE OF DATE;
tab_holiday l_holiday;
BEGIN
SELECT a.dt BULK COLLECT
INTO tab_holiday
FROM
(SELECT SYSDATE dt FROM DUAL
UNION
SELECT SYSDATE+1 dt FROM DUAL
UNION
SELECT SYSDATE+2 dt FROM DUAL
UNION
SELECT SYSDATE+3 FROM DUAL
)a;
IF tab_holiday.COUNT > 0 THEN
IF SYSDATE MEMBER OF tab_holiday THEN
dbms_output.put_line('yes working');
ELSE
dbms_output.put_line('awsme working');
END IF;
END IF;
END;

Resources