Function to return COUNT value without COUNT - sql

The requirements are as follows:
Create a PL/SQL function called findtotalcarmodels to return the total number of cars belonging to a particular model. The function should have a single IN parameter as model_name. You should then use an explicit cursor to count the number of cars belonging to that car model and return the final count. You must NOT use any implicit cursors, table joins, subqueries, set operators, group functions or SQL functions (such as COUNT) to create this function.
The code I have come up with so far is:
CREATE OR REPLACE Function findtotalcarmodels
(model_name IN varchar2)
RETURN NUMBER
IS
CURSOR car_count_cur IS
SELECT model_name FROM i_car;
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;
END LOOP;
CLOSE car_count_cur;
RETURN Rec_car_details;
END;
I am getting the following errors:
Errors for FUNCTION FINDTOTALCARMODELS:
LINE/COL ERROR
15/1 PL/SQL: Statement ignored
15/8 PLS-00382: expression is of wrong type
What am I doing wrong here?

You are trying to return the cursor from the inline function, and the function is looking to return an integer.
You need an (integer) counter to increment each time you iterate through the cursor... and then you can return that. I didn't test this, but this should work:
CREATE OR REPLACE Function findtotalcarmodels
(model_name_in IN varchar2)
RETURN NUMBER
IS
DECLARE 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;

CREATE OR REPLACE FUNCTION findtotalcarmodels (vc_model_name IN VARCHAR2)
RETURN NUMBER
AS
CURSOR c1
IS
SELECT *
FROM i_car
WHERE UPPER (model_name) = UPPER (vc_model_name);
cnt NUMBER;
BEGIN
cnt := 0;
FOR i IN c1 LOOP
cnt := cnt + 1;
END LOOP;
RETURN cnt;
END;
You can check in dual table such as,
SELECT findtotalcarmodels('SUV1') FROM DUAL; -- here suv1 is your modelname

Related

How do i use a cursor correctly

With the code below I am trying to search for certain words (SEED WORDS) inside reviews. Then depending on the number of Seed words inside each review I come up with a rating number. There are multiple reviews per hotel ID. I am trying to get this entered into a Ratings table that has the following columns (HOTEL_ID, CATEGORY_ID,RATING). Each Seed Word has a category associated with it 1,2,3 or 4. The error I have received so far says 'LV_RATE_NUM' must be declared.
DECLARE
lv_hotel NUMBER(4,0);
lv_rate_num NUMBER(4);
lv_rate NUMBER(1,1);
lv_pol NUMBER(1,1);
i number(4,0);
CURSOR cur_rate IS
SELECT a.IDHOTEL, INSTR(a.review,b.seed_words), b.polarity
INTO lv_hotel, lv_rate_num,lv_pol
FROM review a, SEEDWORDS b;
BEGIN
i :=1;
lv_hotel := i;
FOR rec_hotel IN cur_rate LOOP
IF rec_hotel.lv_rate_num > 0
THEN lv_rate := lv_rate_num;
ELSIF rec_hotel.lv_rate_num = 0
THEN lv_rate_num := 8.6;
i := i+1;
END IF;
END LOOP;
INSERT INTO RATINGS
VALUES (lv_hotel, 'CATID',lv_rate);
END;
The INTO clause is only used in a singleton SELECT - that is, a SELECT which only returns one row. Your code can be simplified quite a bit:
DECLARE
i NUMBER := 1;
lv_hotel NUMBER := 1;
lv_rate NUMBER;
BEGIN
FOR rec_hotel IN (SELECT INSTR(a.review, b.seed_words) AS LV_RATE_NUM
FROM review a
CROSS JOIN SEEDWORDS b)
LOOP
IF rec_hotel.LV_RATE_NUM > 0 THEN
lv_rate := rec_hotel.LV_RATE_NUM;
ELSIF rec_hotel.LV_RATE_NUM = 0 THEN
lv_rate := 8.6;
i := i+1;
END IF;
END LOOP;
INSERT INTO RATINGS(HOTEL_ID, CATEGORY_ID,RATING)
VALUES (lv_hotel, 'CATID', lv_rate);
END;
I strongly recommend that you avoid specifying precision and scale on numbers. They are very seldom needed and are a source of potential problems. Note that I'm pretty sure that this still won't do what you intend, but you can use it as a way to get started.
You are getting the error because you have used into in cursor query. You can give alias to the cursor columns and use that column names in the loop.
DECLARE
lv_hotel NUMBER(4,0);
lv_rate_num NUMBER(4);
lv_rate NUMBER(1,1);
lv_pol NUMBER(1,1);
i number(4,1);
CURSOR cur_rate IS
SELECT a.IDHOTEL, INSTR(a.review,b.seed_words) as rate_num, b.polarity as pol
--INTO lv_hotel, lv_rate_num,lv_pol
FROM review a, SEEDWORDS b;
BEGIN
i :=1;
lv_hotel := i;
FOR rec_hotel IN cur_rate LOOP
IF rec_hotel.rate_num > 0 -- used cursor column alias name
THEN lv_rate := lv_rate_num; -- lv_rate_num is not initialized and you are using it here. You must Consider changing the logic
ELSIF rec_hotel.rate_num = 0
THEN lv_rate_num := 8.6;
i := i+1;
END IF;
END LOOP;
INSERT INTO RATINGS
VALUES (lv_hotel, 'CATID',lv_rate);
END;
Your Insert statement is outside the loop so it will insert only one record in the table.
Cheers!!

PL SQL Cursor For Loop: add up values in same variable and output the summed total

I'm trying to calculate the GPA within a function that accepts a studentID as an input. My issue is that the variable lv_gpa_calc isn't adding to itself when the loop works through the cursor set. I added the DBMS_OUTPUT.PUT_LINE to make sure that it's working through the cursor set correctly and that prints to the screen the correct individual row values of what lv_gpa_calc should be, but when it gets returned in the function in a SQL block, it isn't adding all of those values together. Can you not set a variable to itself within a CURSOR FOR LOOP?
Update: initializing the lv_gpa_calc fixed the problem where the variable value wasn't adding to itself.
CREATE OR REPLACE
FUNCTION CALCULATE_GPA
(p_studentID IN number)
RETURN NUMBER
IS
CURSOR cur_gpa IS
SELECT grade, grade_value, credit_hours
FROM grade
JOIN enrollment USING (Grade)
JOIN section USING (term_code, subject_code, course_number, section)
JOIN course USING (subject_code, course_number)
WHERE student_ID = p_studentID;
lv_gpa_calc NUMBER(4,2):=0;
BEGIN
FOR rec_gpa IN cur_gpa LOOP
lv_gpa_calc:= lv_gpa_calc + ((rec_gpa.grade_value * rec_gpa.credit_hours)/rec_gpa.credit_hours);
DBMS_OUTPUT.PUT_LINE(lv_gpa_calc);
END LOOP;
RETURN lv_gpa_calc;
END CALCULATE_GPA;
The problem in your code is that variable lv_gpa_calc is not initialized. Adding whatever to NULL will result as NULL.
Simplified working test case:
--DROP TABLE my_numbers;
CREATE TABLE my_numbers (
id NUMBER
);
/
BEGIN
FOR l_i IN 1..10 LOOP
INSERT INTO my_numbers VALUES (DBMS_RANDOM.RANDOM);
END LOOP;
COMMIT;
END;
/
SELECT * FROM my_numbers;
/
DECLARE
CURSOR cur IS
SELECT id
FROM my_numbers;
l_sum NUMBER(10) := 0;
BEGIN
FOR rec IN cur LOOP
l_sum := l_sum + rec.id;
DBMS_OUTPUT.PUT_LINE('l_sum = ' || l_sum);
END LOOP;
DBMS_OUTPUT.PUT_LINE('Sum = ' || l_sum);
END;
/
Important line is:
l_sum NUMBER(10) := 0;
Without initialization :=0 it won't work.

PLS-00306 on Oracle function using a UDT

I am getting this error:
LINE/COL ERROR
-------- -----------------------------------------------------------------
13/3 PL/SQL: Statement ignored
13/13 PLS-00306: wrong number or types of arguments in call to 'JOIN_JT'
Types used:
CREATE TYPE join_t IS OBJECT (
inn NUMBER(38),
out NUMBER(38)
);
/
CREATE TYPE join_jt IS TABLE OF join_t;
/
Here is the PL/SQL code from the function that is returning the error. When I try to pass the results I have got in join_table to retval the type error above is triggered):
CREATE OR REPLACE FUNCTION join RETURN join_jt
AS
CURSOR cur_fv_table IS SELECT id,fv FROM london WHERE id <= 3000;
retval join_jt := join_jt ();
var_fv london.fv%type;
var_id london.id%type;
join_table join_jt := join_jt();
BEGIN
OPEN cur_fv_table;
LOOP
FETCH cur_fv_table INTO var_id,var_fv;
SELECT join_t(r.id, var_id) BULK COLLECT INTO join_table
FROM london r
WHERE manh_dist(r.fv,var_fv) <= 5;
retval.EXTEND;
retval := join_t(join_table);
END LOOP;
RETURN join_table;
END;
/
You can use this function for testing the function above:
CREATE OR REPLACE FUNCTION manh_dist(
fv1 LONDON.FV%TYPE,
fv2 LONDON.FV%TYPE
) RETURN NUMBER
AS
BEGIN
RETURN 0; -- Implement this.
END;
/
Does anyone know how to solve this error?
I am using the Oracle 11g.
So this is your problem:
retval := join_t (join_table);
You're trying to cast a table to an object type. Which is wrong. To populate the output table you need to union the query collection with the return collection. MULTISET UNION is what you need:
CREATE OR REPLACE FUNCTION knn_join RETURN join_jt
IS
CURSOR cur_fv_table IS SELECT id,fv FROM londonfv WHERE id <= 3000;
retval join_jt := join_jt ();
var_fv londonfv.fv%type;
var_id londonfv.id%type;
join_table join_jt := join_jt();
BEGIN
OPEN cur_fv_table;
LOOP
FETCH cur_fv_table INTO var_id,var_fv;
SELECT join_t(r.id, var_id) BULK COLLECT
INTO join_table FROM londonfv r WHERE manhattan_dist(r.fv,var_fv) <=5;
retval := retval multiset union all join_table;
END LOOP;
RETURN retval;
END;
/
Note: I assume you really meant to return the aggregated collection retval rather than the last intermediate set.
Not having time to test this right now, I admit #Wernfried has given me some doubt as to whether this will run. If you run into problems, this blunter approach will work:
for idx in join_table.first()..join_table.last()
loop
Retval.extend();
retval(retval.count()) := join_table(idx);
end loop;
The mistake you are making is while storing the result. See my comments inline
retval := join_t (join_table);
CREATE OR REPLACE FUNCTION knn_join
RETURN join_jt
IS
CURSOR cur_fv_table
IS
SELECT id, fv
FROM londonfv
WHERE id <= 3000;
retval join_jt := join_jt ();
var_fv londonfv.fv%TYPE;
var_id londonfv.id%TYPE;
join_table join_jt := join_jt ();
BEGIN
OPEN cur_fv_table;
LOOP
--Fetching records of cursor to variable var_id & var_fv
FETCH cur_fv_table INTO var_id, var_fv;
SELECT join_t (r.id, r.fv) -- You made mistake here. You need to select your table columns here not any variable.
BULK COLLECT INTO join_table --- Populating the collection
FROM londonfv r
WHERE manhattan_dist (var_id, var_fv) <= 5; -- Checking from the function
--- Assuming there is only 1 record in collection join_table.
retval.EXTEND;
--- Storing the value of into the collection
retval := join_table;
/* If there are more then
for rec in 1..join_table.count
loop
retval.EXTEND;
retval(rec):= join_table(rec);
end loop;
*/
END LOOP;
RETURN retval;
END;
/

how to get value of column from sys_refcursor returned by a function being called

I am calling a function pck_my_pack.f_my_func in my anonymous block , f_my_func returns a sys_refcursor.
I want the value of req_column from sys_refcursor to used as
IF req_column>0 THEN
do this and that
END IF;
Problem is sys_refcursor which is being returned by my_func does not select values from a specific table. it selects values from multiple tables and few locals variables which were calculated in my_func.
Please help me out to get the value from this sys_refcursor.
Here is what I am trying:
DECLARE
TYPE cu_income_detail is ref cursor;
income_det cu_income_detail;
BEGIN
income_det := pck_my_pack.f_my_func(2542586);
FOR CURRVAL IN income_det LOOP -- I have also tried CUR_VAL
IF CURRVAL.pcls_paid_as_income > 0 THEN
-- Logic yet to be implemented once get the value
dbms_output.put('PCLS paid');
dbms_output.put_line(CUR_VAL.pcls_paid_as_income);
END IF;
END LOOP;
END;
declare
src_cur sys_refcursor;
curid NUMBER;
v_jobtitle varchar2(35);
v_min_salary number;
begin
open src_cur for 'select job_id,job_title,min_salary,max_salary from jobs';
curid := DBMS_SQL.TO_CURSOR_NUMBER(src_cur);
dbms_sql.define_column(curid,2,v_jobtitle,35);
dbms_sql.define_column(curid,3,v_min_salary);
WHILE DBMS_SQL.FETCH_ROWS(curid) > 0 LOOP
DBMS_SQL.COLUMN_VALUE(curid, 2, v_jobtitle);
DBMS_SQL.COLUMN_VALUE(curid, 3, v_min_salary);
DBMS_OUTPUT.put_line('job_titile='||v_jobtitle||' ;min_salary='||v_min_salary);
END LOOP;
DBMS_SQL.CLOSE_CURSOR(curid);
end;
You have to know type and name of columns in your sys_refcursor.
And perform fetch into operation on it.
DECLARE
TYPE cu_income_detail is ref cursor;
income_det cu_income_detail;
BEGIN
income_det := pck_my_pack.f_my_func(2542586);
loop
fetch income_det into var_1, var_2 ... var_3;
-- Logic here
exit when income_det%notfound;
end loop;
END;

PLS-00103 Oracle stored procedure error

I am new to stored procedures.
I am trying to run stored procedure and getting these errors:
I am getting PLS-00103: Encountered the symbol "SELECT" when expecting one of the following: begin function pragma procedure...
PLS-00103: Encountered the symbol "RETURN" when expecting one of the following: * & = - + < / > at in is mod remainder not rem then...
I have tried searching for what causes these errors and for examples similar to this, but results were not sufficient. Any clues as to why these errors are happening?
here is the code:
CREATE OR REPLACE PROCEDURE LIST_ACTIONS_CHECK_ADD
(
LISTNAME IN VARCHAR2
) AS
BEGIN
DECLARE CNT NUMBER;
SELECT COUNT(LIST_NAME) INTO CNT FROM LISTS_MASTER WHERE LIST_NAME = LISTNAME;
IF (CNT > 0)
RETURN 1
ELSE
RETURN 0
END IF;
END LIST_ACTIONS_CHECK_ADD;
New Code:
CREATE OR REPLACE PROCEDURE LIST_ACTIONS_CHECK_ADD
(
P_LISTNAME IN VARCHAR2
)
AS
L_CNT NUMBER;
BEGIN
SELECT COUNT(LIST_NAME)
INTO L_CNT
FROM LISTS_MASTER
WHERE LIST_NAME = P_LISTNAME;
IF (L_CNT > 0)
RETURN 1;
ELSE
RETURN 0;
END IF;
END LIST_ACTIONS_CHECK_ADD;
The skeleton of a stored procedure declaration is
CREATE OR REPLACE PROCEDURE procedure_name( <<parameters>> )
AS
<<variable declarations>>
BEGIN
<<code>>
END procedure_name;
In the code you posted,
You put the BEGIN before the variable declarations
You have an extraneous DECLARE-- you would only use that if you are declaring a PL/SQL block that doesn't involve a CREATE.
You are missing semicolons after your RETURN statements.
A procedure cannot return a value. If you want to return either a 1 or a 0, you probably want a function, not a procedure. If you need a procedure, you can declare an OUT parameter.
You are missing the THEN after the IF
It sounds like you want something like
CREATE OR REPLACE FUNCTION LIST_ACTIONS_CHECK_ADD
(
LISTNAME IN VARCHAR2
)
RETURN NUMBER
AS
CNT NUMBER;
BEGIN
SELECT COUNT(LIST_NAME)
INTO CNT
FROM LISTS_MASTER
WHERE LIST_NAME = LISTNAME;
IF (CNT > 0)
THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END LIST_ACTIONS_CHECK_ADD;
Note that as a general matter, you are generally better off using some sort of naming convention to ensure that parameters and local variables do not share the name of a column. Trying to figure out whether LISTNAME is a parameter or a column name and what the difference between LIST_NAME and LISTNAME is will generally confuse future programmers. Personally, I use a p_ prefix for parameters and a l_ prefix for local variables. I would also suggested using anchored types-- lists_master.list_name%type if that is what is being passed in
CREATE OR REPLACE FUNCTION LIST_ACTIONS_CHECK_ADD
(
P_LIST_NAME IN lists_master.list_name%type
)
RETURN NUMBER
AS
L_CNT NUMBER;
BEGIN
SELECT COUNT(LIST_NAME)
INTO L_CNT
FROM LISTS_MASTER
WHERE LIST_NAME = P_LIST_NAME;
IF (L_CNT > 0)
THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END LIST_ACTIONS_CHECK_ADD;
(Correction #1) You cannot return a value in a procedure; LIST_ACTIONS_CHECK_ADD should be dropped and declared as a function in order to return a NUMBER
(Correction #2) You need to move the declaration of CNT as follows (see below)
(Correction #3) You need semicolons on the return statements:
(Correction #4) You need a THEN after IF (CNT > 0) (see below):
DROP PROCEDURE LIST_ACTIONS_CHECK_ADD;
CREATE OR REPLACE FUNCTION LIST_ACTIONS_CHECK_ADD
(
LISTNAME IN VARCHAR2
)
RETURN NUMBER AS
CNT NUMBER;
BEGIN
SELECT COUNT(LIST_NAME) INTO CNT FROM LISTS_MASTER WHERE LIST_NAME = LISTNAME;
IF (CNT > 0) THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END LIST_ACTIONS_CHECK_ADD;
This Can be executed from SQLPLUS as:
SET SERVEROUTPUT ON SIZE 100000;
DECLARE
V_RESULT NUMBER;
BEGIN
V_RESULT := LIST_ACTIONS_CHECK_ADD('X');
DBMS_OUTPUT.PUT_LINE('RESULT: ' || V_RESULT);
END;