counting rows from a cursor in pl/sql - 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;

Related

I can't call procedures several times

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

Too many values inside a Where

I want to get all products inside an order. I give to my procedure the ID of the order and then I want to list all of them.
Here is the database scheme of the tables I use in this procedure:
And here is the procedure:
CREATE OR REPLACE PROCEDURE exercitiu6(v_ID_Comanda Comanda.ID_Comanda%TYPE) AS
TYPE produse IS TABLE OF Produs%ROWTYPE INDEX BY PLS_INTEGER;
p produse;
TYPE imbricat IS TABLE OF ProduseComanda.ID_Produs%TYPE;
imbricat_produse imbricat:= imbricat();
prod Produs%ROWTYPE;
i number:=0;
j number:=0;
BEGIN
SELECT ID_Produs BULK COLLECT INTO imbricat_produse FROM ProduseComanda
WHERE ID_Comanda = v_ID_Comanda;
FOR i IN imbricat_produse.FIRST..imbricat_produse.LAST LOOP
SELECT ID_Produs, nume, pret INTO prod FROM Produs
WHERE ID_Produs = imbricat_produse(i);
p(j):= prod;
j:= j + 1;
END LOOP;
DBMS_OUTPUT.PUT_LINE('Comanda cu ID-ul ' || v_ID_Comanda || ' contine urmatoarele produse: ');
FOR j IN p.FIRST..p.LAST LOOP
DBMS_OUTPUT.PUT_LINE(p(j).nume);
END LOOP;
END;
I get the Error Sql Statement ignored; too many values on this line:
WHERE ID_Comanda = v_ID_Comanda;
How do I solve this error?
Use a JOIN and a single cursor:
CREATE OR REPLACE PROCEDURE exercitiu6(
v_ID_Comanda ProduseComanda.ID_Comanda%TYPE
)
AS
BEGIN
DBMS_OUTPUT.PUT_LINE('Comanda cu ID-ul ' || v_ID_Comanda || ' contine urmatoarele produse: ');
FOR i IN (
SELECT p.nume
FROM ProduseComanda c
INNER JOIN Produs p
ON p.ID_Produs = c.ID_Produs
WHERE c.ID_Comanda = v_ID_Comanda
)
LOOP
DBMS_OUTPUT.PUT_LINE(i.nume);
END LOOP;
END;
/
Then, for the sample data:
CREATE TABLE produsecomanda (ID_Produs, ID_Comanda) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL;
CREATE TABLE produs (ID_Produs, nume) AS
SELECT 1, 'Alice' FROM DUAL UNION ALL
SELECT 2, 'Beryl' FROM DUAL UNION ALL
SELECT 3, 'Carol' FROM DUAL;
Then:
BEGIN
DBMS_OUTPUT.ENABLE();
exercitiu6(1);
END;
/
Outputs:
Comanda cu ID-ul 1 contine urmatoarele produse:
Alice
Beryl
Carol
If you want to fix your code then the error is not with the WHERE clause but with the mismatch between the number of columns in the SELECT clause and the INTO clause. To fix it you need to use SELECT * INTO ... rather than naming all the columns when you are working with %ROWTYPE variables:
CREATE OR REPLACE PROCEDURE exercitiu6(
v_ID_Comanda ProduseComanda.ID_Comanda%TYPE
)
AS
TYPE produse IS TABLE OF Produs%ROWTYPE INDEX BY PLS_INTEGER;
p produse;
TYPE imbricat IS TABLE OF ProduseComanda.ID_Produs%TYPE;
imbricat_produse imbricat;
i number:=0;
j number:=0;
BEGIN
SELECT ID_Produs
BULK COLLECT INTO imbricat_produse
FROM ProduseComanda
WHERE ID_Comanda = v_ID_Comanda;
FOR i IN imbricat_produse.FIRST..imbricat_produse.LAST LOOP
j:= j + 1;
SELECT *
INTO p(j)
FROM Produs
WHERE ID_Produs = imbricat_produse(i);
END LOOP;
DBMS_OUTPUT.PUT_LINE('Comanda cu ID-ul ' || v_ID_Comanda || ' contine urmatoarele produse: ');
FOR j IN p.FIRST..p.LAST LOOP
DBMS_OUTPUT.PUT_LINE(p(j).nume);
END LOOP;
END;
/
fiddle

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

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;

Calling a function from an explicit cursor

I need to call the stored function findtotalcarmodels from this PL/SQL block. The way this code is written is not the way I would do it in production, however it is an exercise in 'lateral' thinking.
SET SERVEROUTPUT ON FORMAT WRAP SIZE 12000
Declare
v_model VARCHAR2(40);
v_cost NUMBER;
v_reg VARCHAR2(10);
v_carcategory VARCHAR2(40);
v_totalcars NUMBER;
v_count DATE;
v_maxcount DATE;
v_maxdept VARCHAR2(20);
cursor carcur IS
SELECT * FROM i_car;
v_car carcur%ROWTYPE;
Cursor c_date (p_reg i_booking.registration%TYPE) IS
SELECT date_reserved
FROM i_booking
WHERE registration = p_reg;
v_date c_date%ROWTYPE;
Begin
v_totalcars := findtotalcarmodels();
FOR v_car IN carcur LOOP
If v_cost <=50000 THEN v_carcategory := 'Budget Car';
End IF;
If v_cost BETWEEN 50000 AND 100000 THEN v_carcategory := 'Standard Car';
End IF;
If v_cost >100000 THEN v_carcategory := 'Premium Car';
End If;
FOR v_date IN c_date(v_car.registration) LOOP
v_count := v_count + 1;
END LOOP;
IF v_count > v_maxcount THEN
v_maxcount := v_count;
v_maxdept := v_car.registration;
END IF;
DBMS_OUTPUT.PUT_LINE('Registration:'|| ' '|| v_car.registration);
DBMS_OUTPUT.PUT_LINE('Cost:'|| '$' ||v_car.Cost);
DBMS_OUTPUT.PUT_LINE('Model Name:'|| ' '||v_car.model_name);
DBMS_OUTPUT.PUT_LINE('Car Category:'|| ' '||v_carcategory);
DBMS_OUTPUT.PUT_LINE('Total number of Cars:'|| ' '||v_totalcars);
DBMS_OUTPUT.PUT_LINE('Most Recent Rental Date: '|| ' '||v_maxcount);
DBMS_OUTPUT.NEW_LINE;
END LOOP;
END;
I am getting the error:
v_totalcars := findtotalcarmodels();
*
ERROR at line 19:
ORA-06550: line 19, column 16:
PLS-00306: wrong number or types of arguments in call to 'FINDTOTALCARMODELS'
ORA-06550: line 19, column 1:
PL/SQL: Statement ignored
Am I calling my function correctly in the right position?
This is the function:
CREATE OR REPLACE Function findtotalcarmodels
(model_name_in IN varchar2)
RETURN NUMBER
IS
counter INTEGER := 0;
CURSOR car_count_cur IS
SELECT model_name FROM i_car WHERE model_name = model_name_in;
Rec_car_details car_count_cur%ROWTYPE;
BEGIN
OPEN car_count_cur;
LOOP
FETCH car_count_cur INTO Rec_car_details;
EXIT WHEN car_count_cur%NOTFOUND;
counter := counter + 1;
END LOOP;
CLOSE car_count_cur;
RETURN counter;
END;
Okay, so I have no idea why you're getting that error with that function. The error indicates that you're not giving the function the correct number of arguments. Judging by the function that's clearly not what's happening, or it's not the same function.
You've just changed the function call; the function requires an argument so the "incorrect" code you had in your first revision was actually correct.
However, let's put that to one side for a second and look again at what you're doing.
Your function is a count on a table. There's no need for a cursor or looping, incrementing variables or anything. You can simplify it to
select count(*) from i_car where model_name = :model_name
You never assign the variables v_count or v_maxcount a value so incrementing them will still result in a NULL. They're dates anyway, so it's a little strange to be incrementing them.
Your cursor c_date is just another count; once again no need for a loop.
The model_name variable is never initialised so your function will not return a result.
There are a lot of ways to simplify this; though I'm going to guess a few things here. Change your carcur cursor to the following:
select i.*
, case cost
when <= 50000 then 'Budget Car'
when <= 100000 then 'Standard Car'
else 'Premium Car'
end as category
, count(*) over ( partition by model_name ) as total_cars
from i_cars
This appears to enable you to remove your IF statements and your function. You can then remove your second loop by performing an outer join on i_booking (you need to add the primary key in yourself):
select i.*
, case c.cost
when <= 50000 then 'Budget Car'
when <= 100000 then 'Standard Car'
else 'Premium Car'
end as category
, count(distinct c.primary_key)
over ( partition by c.model_name ) as total_cars
, count(b.date_reserved)
over ( partition by b.registration ) as reserved_ct
from i_cars c
left outer join i_booking b
on c.registration = b.registration
I'm not 100% certain on the max stuff as it's not clear at all where it's assigned (it's not) but it looks as though you might be wanting to find the maximum count by model etc, in which case you can use a sub-query on the above cursor:
select sub.*
, max(total_cars) over () as max_cars
, max(reserved_ct) over () as max_reserved
from ( select i.*
, case c.cost
when <= 50000 then 'Budget Car'
when <= 100000 then 'Standard Car'
else 'Premium Car'
end as category
, count(distinct c.primary_key)
over ( partition by c.model_name ) as total_cars
, count(b.date_reserved)
over ( partition by b.registration ) as reserved_ct
from i_cars c
left outer join i_booking b
on c.registration = b.registration
) sub
If you then need to output it (there's rarely a need) you can loop through this single SQL statement, which gives you everything in one go:
declare
c_cursor is
select sub.*
, max(total_cars) over () as max_cars
, max(reserved_ct) over () as max_reserved
from ( select i.*
, case c.cost
when <= 50000 then 'Budget Car'
when <= 100000 then 'Standard Car'
else 'Premium Car'
end as category
, count(distinct c.primary_key)
over ( partition by c.model_name ) as total_cars
, count(b.date_reserved)
over ( partition by b.registration ) as reserved_ct
from i_cars c
left outer join i_booking b
on c.registration = b.registration
) sub
;
begin
for i in c_cursor loop
dbms_output.put_line(i.registration);
dbms_output.put_line(i.cost);
...
end loop;
end;
I've reduced a PL/SQL block and a function to a single SQL statement; it may not be spot on because there's so many unknowns but it's worth trying for yourself. Simple is almost always better.