I can't call procedures several times - sql

I have 2 procedures: the first one is to fill a table "dm.dm_account_turnover_f" with data on sertain day and the second one is to log this procces in a table dm.lg_messages. The first one calls the second one and when I call my procedure several times (I need that to fill my table with all days in a month) it causes the error that the transaction wasn't finished correctly.
When I call my procdure only once there's no errors. How can I do it in a loop without error like that? Is there's something wrong in procedures?
PROCEDURE ds.fill_account_turnover_f()
declare
v_RowCount int;
begin
call dm.writelog( '[BEGIN] fill(i_OnDate => date '''
|| to_char(i_OnDate, 'yyyy-mm-dd')
|| ''');', 1
);
call dm.writelog( 'delete on_date = '
|| to_char(i_OnDate, 'yyyy-mm-dd'), 1
);
delete
from dm.dm_account_turnover_f f
where f.on_date = i_OnDate;
call dm.writelog('insert', 1);
insert
into dm.dm_account_turnover_f
( on_date
, account_rk
, credit_amount
, credit_amount_rub
, debet_amount
, debet_amount_rub
)
with wt_turn as
( select p.credit_account_rk as account_rk
, p.credit_amount as credit_amount
, p.credit_amount * nullif(er.reduced_cource, 1) as credit_amount_rub
, cast(null as numeric) as debet_amount
, cast(null as numeric) as debet_amount_rub
from ds.ft_posting_f p
join ds.md_account_d a
on a.account_rk = p.credit_account_rk
left
join ds.md_exchange_rate_d er
on er.currency_rk = a.currency_rk
and i_OnDate between er.data_actual_date and er.data_actual_end_date
where p.oper_date = i_OnDate
and i_OnDate between a.data_actual_date and a.data_actual_end_date
and a.data_actual_date between date_trunc('month', i_OnDate) and (date_trunc('MONTH', to_date(i_OnDate::TEXT,'yyyy-mm-dd')) + INTERVAL '1 MONTH - 1 day')
union all
select p.debet_account_rk as account_rk
, cast(null as numeric) as credit_amount
, cast(null as numeric) as credit_amount_rub
, p.debet_amount as debet_amount
, p.debet_amount * nullif(er.reduced_cource, 1) as debet_amount_rub
from ds.ft_posting_f p
join ds.md_account_d a
on a.account_rk = p.debet_account_rk
left
join ds.md_exchange_rate_d er
on er.currency_rk = a.currency_rk
and i_OnDate between er.data_actual_date and er.data_actual_end_date
where p.oper_date = i_OnDate
and i_OnDate between a.data_actual_date and a.data_actual_end_date
and a.data_actual_date between date_trunc('month', i_OnDate) and (date_trunc('MONTH', to_date(i_OnDate::TEXT,'yyyy-mm-dd')) + INTERVAL '1 MONTH - 1 day')
)
select i_OnDate as on_date
, t.account_rk
, sum(t.credit_amount) as credit_amount
, sum(t.credit_amount_rub) as credit_amount_rub
, sum(t.debet_amount) as debet_amount
, sum(t.debet_amount_rub) as debet_amount_rub
from wt_turn t
group by t.account_rk;
GET DIAGNOSTICS v_RowCount = ROW_COUNT;
call dm.writelog('[END] inserted ' || to_char(v_RowCount,'FM99999999') || ' rows.', 1);
commit;
end
PROCEDURE dm.writelog(, )
declare
log_NOTICE constant int := 1;
log_WARNING constant int := 2;
log_ERROR constant int := 3;
log_DEBUG constant int := 4;
c_splitToTable constant int := 4000;
c_splitToDbmsOutput constant int := 900;
v_logDate timestamp;
v_callerType varchar;
v_callerOwner varchar;
v_caller varchar;
v_line numeric;
v_message varchar;
begin
v_logDate := now();
-- split to log table
v_message := i_message;
i_messageType := log_NOTICE;
while length(v_message) > 0 loop
insert into dm.lg_messages (
record_id,
date_time,
pid,
message,
message_type,
usename,
datname,
client_addr,
application_name,
backend_start
)
select
nextval('dm.seq_lg_messages'),
now(),
pid,
substr(v_message, 1, c_splitToTable),
i_messageType,
usename,
datname,
client_addr,
application_name,
backend_start
from pg_stat_activity
where pid = pg_backend_pid();
v_message := substr(v_message, c_splitToTable + 1);
end loop;
commit;
end
I need fill my table with data for a month so I need to call my procedure 31 times. I tried this with python
my loop:
date_dt = datetime.strptime(input('Please input the last day of month in format "yyyy-mm-dd" '), "%Y-%m-%d")
while (date_dt - timedelta(days=1)).strftime("%m") == date_dt.strftime("%m"):
date_str = str(date_dt)
new_table.fill_account_turnover_f(date_str)
date_dt -= timedelta(days=1)
function that calls the procedure:
def fill_account_turnover_f(self, date_str):
cur = self.con.cursor()
cur.execute(f"CALL ds.fill_account_turnover_f('{date_str}');")
self.con.commit()
cur.close()
but it causes the error about invalid transaction termitation! it says something about COMMIT, string 51 and string 6 operator CALL

Related

HOW TO DEAL WITH THESE ERROR ,PL/SQL Compilation unit analysis terminated & PSL-00201 : 'TABLE NAME' MUST BE DECLARED

I am creating a function in Oracle but it's not getting compiled and I am getting two errors
Error 1.
Error: PL/SQL: Compilation unit analysis terminated
Error 2.
Error(2,16): PLS-00201: identifier 'DIV_DUR_PRICE_TABLE' must be
declared
HERE IS THE CODE
CREATE OR REPLACE FUNCTION DIV_DAR(FID IN VARCHAR, DATE1 IN DATE, DATE2 IN DATE)
RETURN DIV_DUR_PRICE_TABLE PARALLEL_ENABLE AS
PRAGMA AUTONOMOUS_TRANSACTION;
CNT NUMBER;
V_RET DIV_DUR_PRICE_TABLE;
BEGIN;
EXECUTE IMMEDIATE 'DELETE from GTT_DIV_DUR_PRICE_TABLE';
Insert Into GTT_DIV_DUR_PRICE_TABLE
(select mydate, column1, column2, 0 as Final_value from tablename)
-- please refer my previous question to understand the code written ahead
declare
v_num integer := 1;
v_column1 number(8,2);
v_column2 number(8,2);
v_Final_value number(8,2);
begin
for rec in (select * from GTT_DIV_DUR_PRICE_TABLE order by mydate)
loop
if(v_num = 1) then
update tab set Final_value = column1 where mydate = rec.mydate;
else
if(rec.column2 is not null) then
update tab set Final_value =
v_Final_value * (v_column1/rec.column1) +
rec.column2 * (v_column1/v_Final_value) where mydate = rec.mydate;
else
update tab set Final_value =
v_Final_value * (rec.column1 / v_column1) where mydate = rec.mydate;
end if;
end if;
v_num:= v_num +1;
v_column1 := rec.column1;
v_column2 := rec.column2;
select final_value into v_Final_value from GTT_DIV_DUR_PRICE_TABLE
where mydate = rec.mydate;
end loop;
end;
SELECT
CAST(
MULTISET(
SELECT * FROM GTT_DIV_DUR_PRICE_TABLE order by P_DATE desc
)AS DIV_DUR_PRICE_TABLE
) INTO V_RET FROM DUAL;
COMMIT;
RETURN V_RET;
END DIV_DAR;

using dynamic sql to create column for select statement

I'm writing a stored procedure for paginated results and this result can be ordered by certain values. I did have a switch case in a select statement but because it was trying to do an orderby on rownum it was very slow.
Now I am trying to use dyanmic sql to build the query outside the select but I don't know if what I am doing is possible.
Here is my SQL in Oracle SQL Developer:
create or replace PROCEDURE Sp_tsa_trainees_pagination (
schemeid IN INT,
searchval IN VARCHAR2,
pagesize IN INT DEFAULT 20,
currentpage IN INT DEFAULT 1,
--orderby IN VARCHAR2,
cursor_ OUT SYS_REFCURSOR)
AS
-- LOCAL VARIABLES
totalcount INT;
numberofpages INT;
startposition NUMBER;
endposition NUMBER;
orderby VARCHAR2(100) := 'surname asc' ;
dynamic_query VARCHAR(255) := 'row_number() over (order by t.SURNAME DESC, t.FORENAMES DESC) AS rnum';
BEGIN
-- Get total number of trainees in scheme
select COUNT(t.ORG_REGISTRATION_ID)
into totalcount FROM v_trainee t
where t.ORG_REGISTRATION_ID = schemeid
AND t.status = 'A' and LOWER(t.trainee_name) like '%' || LOWER(searchval) || '%';
-- calculate number of pages in the pagination by dividing total number of records by how many to display for each page
numberofpages := totalcount / pagesize;
-- get start position by multiplying number of records to display for each page by current page
startposition := pagesize *( currentpage-1);
-- add calculated start position by number of records to display to get end position
endposition := startposition + pagesize;
CASE orderby
WHEN 'surname desc' THEN dynamic_query := 'row_number() over (order by t.SURNAME DESC, t.FORENAMES DESC) AS rnum';
WHEN 'surname asc' THEN dynamic_query := 'row_number() over (order by t.SURNAME ASC, t.FORENAMES ASC) AS rnum';
END CASE;
OPEN cursor_ FOR
Select * from
(
SELECT
-- order by based on selection
dynamic_query rnum,
t.ORG_REGISTRATION_ID SearchId,
t.FORENAMES Forenames,
t.FORENAME Forename,
t.SURNAME Surname,
t.person_id PersonId,
t.trainee_name TraineeName,
t.STATUS Status,
t.IPD_ANNUAL_REVIEW_DATE AnnualReviewDate,
t.ANNUAL_REVIEW_STATUS AnnualReviewStatus,
t.payment_received PaymentRecieved,
t.TRAINEE_ID TraineeId,
t.IPD_SIGNUP_DATE IpdSignupDate,
t.START_DATE StartDate,
t.END_DATE EndDate,
t.LENGTH_ON_SCHEME LengthOnScheme,
t.EMPLOYEE_NUMBER EmploymentNumber,
t.SELECTED_LEVEL SelectedLevel,
t.SELECTED_LEVEL_DESCRIPTION SelectedLevelDescription,
t.ELIGIBLE_LEVEL EligibleLevel,
t.ELIGIBLE_LEVEL_DESCRIPTION EligibleLevelDescription,
sce.FORENAMES SceForenames,
sce.FORENAME SceForename,
sce.SURNAME SceSurname,
sce.mentor_name SceName,
sce.EMPLOYEE_NUMBER SceEmployeeNumber,
de.FORENAMES DeForenames,
de.FORENAME DeForename,
de.SURNAME DeSurname,
de.mentor_name DeName,
de.EMPLOYEE_NUMBER DeEmployeeNumber,
t.COMPLETED_ATTRIBUTE_LEVELS CompletedAttributeLevels,
t.ATTRIBUTE_LEVEL_COUNT AttributeLevelCount,
-- get percentage
CASE t.ATTRIBUTE_LEVEL_COUNT
WHEN 0 THEN 0
ELSE
COMPLETED_ATTRIBUTE_LEVELS / t.ATTRIBUTE_LEVEL_COUNT * 100
END percentage,
DECODE(F_ISTRAINEEGROUPMEMBER(t.ORG_REGISTRATION_ID, 'S', t.person_id),'Y','N','Y') WithoutTsaGroup,
orr.status SchemeStatus,
(select count(*) from TRAINING_GROUP_TRAINEE tgt where tgt.trainee_id = t.TRAINEE_ID) NUMBER_OF_GROUPS,
TotalCount
FROM v_trainee t
INNER JOIN org_registration orr ON t.ORG_REGISTRATION_ID = orr.id
LEFT OUTER JOIN v_mentor sce ON t.sce_id = sce.MENTOR_ID
LEFT OUTER JOIN v_mentor de ON t.de_id = de.MENTOR_ID
where t.ORG_REGISTRATION_ID = schemeid AND t.status = 'A'
and LOWER(t.trainee_name) like '%' || LOWER(searchval) || '%'
)
where rnum >= startposition and rnum <= endposition;
END;
I want to use this variable with the assigned sql:
dynamic_query rnum,
But when I execute the stored procedure I get this error:
ORA-01722: invalid number ORA-06512: at
"db.SP_TSA_TRAINEES_PAGINATION", line 46 ORA-06512: at line 13
So basically my question is can I assign a SQL to VARCHAR2 and then use it in a select statement dynamically.
You may need dynamic SQL for this. For example:
create or replace procedure testDyn(n in number, C OUT SYS_REFCURSOR) is
vDynamicPart varchar2(1000);
vSQl varchar2(1000);
begin
--
if (n = 1) then
vDynamicPart := 'count(1)';
else
vDynamicPart := 'count(null)';
end if;
--
vSQl := 'select ' || vDynamicPart || ' from dual';
open C for vSQl;
end;
If you call it
declare
n1 number;
n2 number;
C1 SYS_REFCURSOR;
C2 SYS_REFCURSOR;
begin
testDyn(1, C1);
testDyn(2, C2);
fetch C1 into n1;
fetch C2 into n2;
dbms_output.put_line('n1: ' || n1);
dbms_output.put_line('n2: ' || n2);
end;
you get:
n1: 1
n2: 0

counting rows from a cursor in pl/sql

I'm trying to count the number of rows that will be returned from an sql statement . This statement is in a cursor
My code is like this
DECLARE
v_counter int := 0 ;
select count(*) into v_counter from (
cursor get_sth is select * from table1 where condit..) ;
BEGIN
DBMS_OUTPUT.PUT_LINE (v_counter);
END ;
/
and it doesn't work
Is there any other solution that makes me counting the number of rows of a cursor result , I'm really noob
thanks helpers
If your aim is to reuse an existing cursor definition and not have to repeat the query it's based on, you could loop over its results to get a count:
set serveroutput on
declare
v_counter pls_integer := 0;
cursor get_sth is select * from all_tables where owner = user; -- your query
begin
for sth in get_sth loop
v_counter := v_counter + 1;
end loop;
dbms_output.put_line (v_counter);
end;
/
You can't count the rows in the result set without fetching them, which the cursor loop does. (#MarcinWroblewski shows another way to that, with explicit fetches). Either way the cursor is consumed by the process. If you want to do anything with the returned data after counting it you'd have to re-execute and re-fetch the cursor.
What about
DECLARE
v_counter INT := 0;
BEGIN
SELECT COUNT(*) INTO v_counter FROM table1 WHERE condit..;
dbms_output.put_line(v_counter);
END;
/
?
If you already have cursor and just want to know how many records it returns, you have to fetch them all
DECLARE
CURSOR get_sth IS
SELECT * FROM table1 WHERE condit..;
sth get_sth%ROWTYPE;
v_counter NUMBER;
BEGIN
OPEN get_sth;
LOOP
FETCH get_sth
INTO sth;
EXIT WHEN get_sth%NOTFOUND;
END LOOP;
v_counter := get_sth%ROWCOUNT;
dbms_output.put_line(v_counter);
CLOSE get_sth;
END;
/
to optimize runnign time take de cursor code and get into the body code
CURSOR MOVIMIENTO_ACTIV IS
SELECT X.CODMODFUE
, X.COD_DEP
, X.CODTIPREC
, SUM(X.VLR_COSTO)
, X.COD_ACTIVIDAD
, X.PERIODO
, Y.CLASE
, Y.ESTADO
, Y.redistri
, X.recurso
, ROWNUM NUMERO
FROM COS_MOVIMIENTO X
, COS_NIVELES Y
WHERE X.EMPRESA = PEMP AND
X.EMPRESA = Y.EMPRESA AND
X.COD_ACTIVIDAD = Y.COD_NIVEL AND
X.PERIODO = PPER AND
Y.CLASE = 'G' AND
Y.ESTADO='A' AND
Y.redistRI = 'S'
GROUP BY X.CODMODFUE
, X.COD_DEP
, X.CODTIPREC
, X.COD_ACTIVIDAD
, X.PERIODO
, Y.CLASE
, Y.ESTADO
, Y.redistri
, X.recurso
;
begin
SELECT COUNT(*) FROM (SELECT X.CODMODFUE
, X.COD_DEP
, X.CODTIPREC
, SUM(X.VLR_COSTO)
, X.COD_ACTIVIDAD
, X.PERIODO
, Y.CLASE
, Y.ESTADO
, Y.redistri
, X.recurso
FROM COS_MOVIMIENTO X
, COS_NIVELES Y
WHERE X.EMPRESA = '01' AND
X.EMPRESA = Y.EMPRESA AND
X.COD_ACTIVIDAD = Y.COD_NIVEL AND
X.PERIODO = '201803' AND
Y.CLASE = 'G' AND
Y.ESTADO='A' AND
Y.redistRI = 'S'
GROUP BY X.CODMODFUE
, X.COD_DEP
, X.CODTIPREC
, X.COD_ACTIVIDAD
, X.PERIODO
, Y.CLASE
, Y.ESTADO
, Y.redistri
, X.recurso
)W
end;

Paging and sorting using a stored procedure in oracle

I have created a stored procedure for paging. Now I am looking for column sorting.
My working paging stored procedure:
PROCEDURE paging (PageSize IN INT,
PageIndex IN INT,
SortColumn IN VARCHAR,
PageData OUT Page) AS
FirstIndex INT;
LastIndex INT;
SortCol VARCHAR;
BEGIN
LastIndex := PageSize * (PageIndex + 1);
FirstIndex := LastIndex - PageSize + 1;
SortCol := SortColumn;
OPEN PageData FOR
SELECT *
FROM (SELECT a.*, ROWNUM AS rnum
FROM ( SELECT *
FROM table_name
ORDER BY SortCol) a
WHERE ROWNUM <= LastIndex)
WHERE rnum >= FirstIndex;
END paging;
/
I would suggest you make use of a wonderful feature called Dynamic SQL (Oracle Docs).
I have also modified your SQL query and used ROW_NUMBER() instead of rownum. It is a more robust method of ordering and numbering the output rows than the latter.
I have also removed a few variables that I don't think were needed from your PL/SQL:
PROCEDURE paging (PageSize IN INT,
PageIndex IN INT,
SortColumn IN VARCHAR2, -- Assuming this always contains
-- the ordering column name
PageData OUT Page) AS
FirstIndex INT;
LastIndex INT;
v_sql VARCHAR2(4000);
BEGIN
LastIndex := PageSize * (PageIndex + 1);
FirstIndex := LastIndex - PageSize + 1;
v_sql := 'SELECT *'
||' FROM (SELECT a.*,'
||' ROW_NUMBER() '
||' OVER (ORDER BY ' || SortColumn || ') AS rnum'
||' FROM table_name a)'
||' WHERE rnum BETWEEN FirstIndex AND LastIndex';
OPEN PageData FOR v_sql;
END paging;
/
This SP alone doing all the three things in Oracle, Paging, Sorting and Filtering of records.
create or replace procedure GetResults
(
p_userId In Number,
p_dueDateFrom In Date,
p_dueDateTo in Date,
p_durationMax in Number,
p_durationMin in Number,
p_sortColumn In Varchar2,
p_sortOrder In Varchar2,
p_pageSize In Number,
p_pageIndex in number,
cv_1 OUT SYS_REFCURSOR
)
as
v_FirstIndex NUMBER;
v_LastIndex NUMBER;
begin
-- Paging
v_LastIndex := p_pageSize * (p_pageIndex + 1);
v_FirstIndex := v_LastIndex - p_pageSize + 1;
OPEN cv_1 FOR
SELECT * FROM (SELECT a.*, ROWNUM AS rnum
FROM (Select * From Newjob nj Where nj.userId = p_userId
-- Filtering
And ((p_dueDateFrom IS NULL AND p_dueDateTo Is NULL) OR
(nj.Due_Date >= p_dueDateFrom and nj.Due_Date <= p_dueDateTo)
)
And ((p_durationMax IS NULL AND p_durationMin Is NULL) OR
(nj.Duration >= p_durationMax and nj.Duration <= p_durationMin)
)
-- Sorting
order by
Case when p_sortOrder = 'Ascending' And p_sortColumn = 'DUE_DATE' then nj.Due_Date End,
Case When p_sortOrder = 'Ascending' And p_sortColumn = 'DURATION' then nj.DURATION end,
Case when p_sortOrder = 'Descending' And p_sortColumn = 'DUE_DATE' then nj.Due_Date End desc,
Case When p_sortOrder = 'Descending' And p_sortColumn = 'DURATION' then nj.DURATION end desc)a
WHERE ROWNUM <= v_LastIndex)
WHERE rnum >= v_FirstIndex;
end;

Filling Time_Dimension?

I wrote this script but it doesn't work. I don't get any error message, it just hangs, could you please help me out?
DECLARE
vYear_ID VARCHAR(4);
vQuarter CHARACTER(6);
vMonth_Num SMALLINT;
vMonth_Name VARCHAR(20);
vWeekday_Num SMALLINT;
vWeekday_Name VARCHAR(20);
vStartDate Date := '01/01/1998';
vEndDate Date := '31/12/2002';
vDate_ID Date;
vDate_ID := vStartDate;
BEGIN
WHILE vDate_ID < vEndDate 
LOOP
vYear_ID := TO_CHAR(vDate_ID,'YYYY');
vQuarter := TO_CHAR(vDate_ID,'YYYY')||'Q'||TO_CHAR(vDate_ID,'Q');
vMonth_Num := TO_NUMBER(TO_CHAR(vDate_ID,'MM'));
vMonth_Name := TO_CHAR(vDate_ID,'Month');
vWeekday_Num := TO_NUMBER(TRIM(leading '0' FROM TO_CHAR(vDate_ID,'D')));
vWeekday_Name := TO_CHAR(vDate_ID,'Day');
INSERT INTO Time_Dim VALUES (vDate_ID, vYear_ID, vQuarter, vMonth_Name, vWeekday_Name, vMonth_Num, );
vDate_ID = to_date(vDate_ID,'DD/MM/YYYY')+1
END LOOP;
END;
I have to use the While Loop and not a For.
Thanks in advance.
This doesn't address the exact question. But as the actual question is a dreary "guess the compilation error" I have elected to answer a more interesting question instead :)
INSERT INTO Time_Dim VALUES
(vDate_ID, vYear_ID, vQuarter, vMonth_Name, vMonth_Num, vWeekday_Name, vWeekday_Num)
with dt as ( select date '1998-01-01' as start_dt
, date '2002-12-31' as end_dt
from dual )
, ths as ( select start_dt + ( level - 1) as id_date
from dt
connect by level <= ( end_dt - start_dt ) + 1
)
select id_date
, to_char(id_date, 'YYYY')
, trim(to_char(id_date, 'YYYY'))||'Q'||to_char(id_date, 'Q')
, to_char(id_date, 'Month')
, to_number(to_char(id_date, 'MM'))
, to_char(id_date, 'Day')
, to_number(to_char(id_date, 'D'))
from ths;
Here's a SQL Fiddle for the SELECT part of the statement.