the question:
Write a blocK PL/SQL that display the total commission amount of a job id. Use function “compute_commission” that accepts a job id equal to 9 and return his total commission of all corresponding employees.
the error:
`Error at line 11: PLS-00103: Encountered the symbol "DECLARE"
CREATE OR REPLACE FUNCTION compute_commission (C_employee_id in number)
RETURN number
is `
the code:
CREATE OR REPLACE FUNCTION compute_commission (C_employee_id in number)
RETURN number
is
sum_commission number;
begin
select sum(job_id)
into sum_commission from employees
where employee_ref_id = C_employee_id;
return sum_commission;
end compute_commission;
declare
cal_sum_commission number;
begin
cal_sum_commission = compute_commission(cal_sum_commission);
dbms_output.put_line ('employee commission is: ' || compute_commission(cal_sum_commission);
end;
Should be something like this:
CREATE OR REPLACE FUNCTION compute_commission (C_employee_id IN NUMBER)
RETURN NUMBER
IS
sum_commission NUMBER;
BEGIN
SELECT SUM (job_id)
INTO sum_commission
FROM employees
WHERE employee_ref_id = C_employee_id;
RETURN sum_commission;
END compute_commission;
/
DECLARE
cal_sum_commission NUMBER := 12345;
BEGIN
cal_sum_commission := compute_commission (cal_sum_commission);
DBMS_OUTPUT.put_line (
'employee commission is: ' || cal_sum_commission);
END;
/
Note that I modified anonymous PL/SQL block and
added local variable's value (otherwise you'd pass NULL to the function) (you'll, of course, use some valid value; this - 12345 - is just an example)
used local variable in DBMS_OUTPUT.PUT_LINE
terminated statement with a semi-colon (you've had a colon)
fixed assignment operator (:= instead of just =)
Also, is sum_commision really sum of JOB_ID values? Looks strange to me ...
Related
I have this function and I need to compare number with varchar.
CREATE OR REPLACE FUNCTION getOdds(i_odd in varchar2, i_id in number) return number as
begin
declare odd integer;
declare i_perecentage=0;
begin
if i_odd ='SP'
then
return (0);
end if;
odd:=round(to_number((1-i_perecentage/100)*i_odd),2);
if odd<1
then
return(i_odd);
else
return(round(odd,2));
end if;
end;
end;
/
PS: I edited function and i resolve problem with comparing , now i have another situation that i dont like..
This function returns calculated percentage of i_odd. The problem is that if i pass 0 in i_percentage in results i get result with no decimal places(for example: i_odd = 3.10 and i_percentage = 0 i get odd = 3 but if I pass i_odd = 3.10 and i_percentage = 1 i get odd = 3.10 ).
Why is on i_percentage = 0 i dont get decimal places ??
If you want to validate a varchar2 field as a number in PL/SQL, typically you'd just try converting it to a number and catch the exception.
CREATE OR REPLACE FUNCTION getOdds(i_odd in varchar2, i_id in number) return number as
odd number;
BEGIN
-- if i_odd = 'SP' (or another non-number), this will throw an ORA-01722
-- exception which will be caught in the exception block, below
odd := to_number(i_odd); -- you might want a format mask here
--... now you can use "odd" as a number
EXCEPTION WHEN INVALID_NUMBER THEN
return 0;
END;
/
You can also nest a begin..end block in the middle of your code just to catch exceptions, if that works better for you:
CREATE OR REPLACE FUNCTION getOdds(i_odd in varchar2, i_id in number) return number as
odd number;
begin
begin
odd := to_number(i_odd); -- you might want a format mask here
exception when INVALID_NUMBER then
odd := 0;
end;
--... now you can use "odd" as a number
end;
/
The reason why you can't catch the invalid_number exception is because you are declaring the input parameter as a number. When you call your function, Oracle tries to convert the string to a number first (and it fails of course, before entering your code at all).
If you change the input parameter to varchar2, then the conversions to number (implicit in this case) is done inside the function, and invalid numbers can be caught and handled as you want (here I'm just returning a different string to denote the issue):
create or replace function is_odd_even(i_num in varchar2)
return varchar2
is
begin
-- conversion to number is done here
if (mod(i_num, 2) = 0) then
return 'EVEN';
else
return 'ODD';
end if;
exception
when INVALID_NUMBER or VALUE_ERROR then
-- do something meaningful
return 'INV';
end;
Usage example:
with x as (
select '1' as val from dual
union all
select 'SP' as val from dual
union all
select '2' as val from dual
)
select x.val, is_odd_even(x.val)
from x;
Output:
1 ODD
SP INV
2 EVEN
SOLUTION:
CREATE OR REPLACE FUNCTION getOdds(i_odd in varchar2, i_id in number) return varchar2 as
odd varchar2(10);
ret_value number(4);
begin
if (i_odd ='SP') or i_odd is null then
return 'SP';
else
odd :=ROUND( TO_NUMBER( ( 1 - (play_beting.get_odds_percentage(i_id) / 100 ) ) * TO_NUMBER(i_odd) ), 2);
IF(odd < 1) THEN
ret_value := TO_NUMBER(i_odd);
ELSE
ret_value := to_char(odd,'9999.00');
END IF;
END IF;
RETURN to_char(ret_value,'9999.00');
END getOdds;
My Question is "How can we return multiple records from pl/sql stored procedure without taking OUT parameter".I got this doubt because if we are using cursors or refcursor in out parameter it may degrade performance.So what is the solution??
As OldProgrammer wrote, i think the performance of a cursor wouldn't be you problem. But here a Solution anyway:
You can return custom types like Table of number. If it's only a list of numbers you could return a table of numbers. If you Want to return rows from a table you could return table of 'tablename'%ROWTYPE. But i guess you want to create some custom types.
CREATE OR REPLACE TYPE PUWB_INT.MyOrderType AS OBJECT
(
OrderId NUMBER,
OrderName VARCHAR2 (255)
)
/
CREATE OR REPLACE TYPE PUWB_INT.MyOrderListType AS TABLE OF MYORDERtype
/
Now we can use them similar to a return myNumberVariable;
Let's build a function (procedures don't have return values):
CREATE OR REPLACE FUNCTION PUWB_INT.MyFunction (SomeInput VARCHAR2)
RETURN MyOrderListType
IS
myOrderList MyOrderListType := MyOrderListType ();
BEGIN
FOR o IN (SELECT 1 AS Id, 'One' AS Name FROM DUAL
UNION ALL
SELECT 2 AS Id, 'Two' AS Name FROM DUAL)
LOOP
myOrderList.EXTEND ();
myOrderList (myOrderList.COUNT) := MyOrderType (o.Id, o.Name || '(' || SomeInput || ')');
END LOOP;
RETURN myOrderList;
END MyFunction;
/
Now we can call the function and get a table of our custom-type:
DECLARE
myOrderList MyOrderListType;
myOrder MyOrderType;
BEGIN
myOrderList := MyFunction ('test');
FOR o IN myOrderList.FIRST .. myOrderList.LAST
LOOP
myOrder := myOrderList (o);
DBMS_OUTPUT.put_line ('Id: ' || myOrder.OrderId || ', Name: ' || myOrder.OrderName);
END LOOP;
END;
Be aware, that the calling schema, has to know the type.
I am trying to create a simple function that takes in 3 parameters, 2 numbers and a string. I have written the function but am not getting the expected results from a simple select statement when using the LIKE comparison for the string.
The select from the function below returns no rows when executed with the string input value set to ebts, but if I run this as a standalone select state it returns 2 rows which what I would expect. Have used dbms output to determine if whitespace were being passed but all looks OK.
CREATE OR REPLACE FUNCTION OPC_OP.sitezone_exists
(in_site_id IN NUMBER, in_zone_id IN NUMBER, in_mod VARCHAR2)
RETURN NUMBER
IS
v_count_rec NUMBER;
v_return NUMBER;
v_mod VARCHAR2(4) := in_mod;
BEGIN
SELECT COUNT(*)
INTO v_count_rec
FROM AW_ACTIVE_ALARMS
WHERE AW_ACTIVE_ALARMS.site_id = in_site_id
AND AW_ACTIVE_ALARMS.zone_id = in_zone_id
AND AW_ACTIVE_ALARMS.module LIKE 'v_mod%';
IF v_count_rec > 0
THEN
DBMS_OUTPUT.PUT_LINE('count'||v_count_rec||'=========='||v_mod||'=============');
v_return:= 1;
RETURN (v_return);
ELSE
DBMS_OUTPUT.PUT_LINE('count'||v_count_rec||'=========='||v_mod||'=============');
v_return:= 0;
RETURN (v_return);
END IF;
END sitezone_exists;
When passing in values 12, 12, ebts the output displayed is:
count 0 ==========ebts=============
RetVal = 0
If I run the same select subtituting only passing in the above values the query returns 2 rows - I have removed the like clause of the function and it then returns 2 rows, any idea why the like part of clause is failing to match with rows.
You are trying to match a string literal with this:
AND AW_ACTIVE_ALARMS.module LIKE 'v_mod%';
Change it to:
AND AW_ACTIVE_ALARMS.module LIKE v_mod||'%';
MarioAna has the right answer, IMO, but as an aside (which is too long for a comment), your function can be better written.
You're duplicating the dbms_output code, plus it's considered best practice to have a single RETURN in a function (although I would argue that more might be ok - one in the body and one per exception in the exception block...), so you could rewrite it as:
CREATE OR REPLACE FUNCTION OPC_OP.sitezone_exists
(in_site_id IN NUMBER, in_zone_id IN NUMBER, in_mod VARCHAR2)
RETURN NUMBER
IS
v_count_rec NUMBER;
v_return NUMBER;
v_mod VARCHAR2(4) := in_mod;
BEGIN
SELECT COUNT(*)
INTO v_count_rec
FROM AW_ACTIVE_ALARMS aaa
WHERE aaa.site_id = in_site_id
AND aaa.zone_id = in_zone_id
AND aaa.module LIKE v_mod||'%';
DBMS_OUTPUT.PUT_LINE('count '||v_count_rec||'=========='||v_mod||'=============');
IF v_count_rec > 0 THEN
v_return := 1;
ELSE
v_return:= 0;
END IF;
RETURN (v_return);
END sitezone_exists;
/
I get this error when I compile my function.
PLS-00103: Encountered the symbol "*" when expecting one of the following:
:= . ( # % ;
Here is the code:
CREATE OR REPLACE FUNCTION IncreaseSalary
(para_empid IN employee.employeeid%TYPE, para_increase IN NUMBER)
RETURN NUMBER
IS
v_SalaryOut NUMBER(10,2);
v_salary2 NUMBER;
BEGIN
SELECT Salary INTO v_Salary2
FROM Employee
WHERE employeeid = para_empid;
--this is the area that pertains to the error
(v_salary2 * para_increase) + v_salary2 = v_salaryout;
RETURN v_SalaryOut;
EXCEPTION
WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('Employee not found.');
END IncreaseSalary;
/
This next part wouldn't be part of the function and is not part of the error
but probably has errors.
DECLARE
v_SalaryOutput NUMBER := IncreaseSalary;
BEGIN
IncreaseSalary('01885', '20%');
DBMS_OUTPUT.PUT_LINE ('Increased Salary: ')
|| TO_CHAR(v_SalaryOutput));
END;
/
The point is to take in two numbers (employeeid and the percentage to increase whatever salary is already listed in the table) and return the updated salary. I don't understand why I can't multiply.
You cannot assign value to variable like that. The target variable should come first. It should be,
v_salaryout := (v_salary2 * para_increase) + v_salary2;
You have declared para_increase as NUMBER. But while calling the function, you are passing '20%' as the value. This will result in numeric or value error: character to number conversion error.
There is an extra bracket in you dbms_output statement.
DBMS_OUTPUT.PUT_LINE ('Increased Salary: ' || TO_CHAR(v_SalaryOutput));
I need to return the names of employees in string format for all those employees whose manager ID depends on the passed parameter. When I compile the function I get an error. Here is the function code:
create or replace function Employee(v_manid IN employees.manager_id%type)
return varchar2
AS
cursor cur_emp is select last_name from employees where manager_id = v_manid;
v_names varchar2(10);
begin
for emp_rec in cur_emp
loop
v_name = v_name || emp_rec.last_name ||', ';
end loop;
return v_name
end;
/
The error is:
Error(8,8): PLS-00103: Encountered the symbol "=" when expecting one
of the following: := . ( # % ; Error(8,44): PLS-00103:
Encountered the symbol ";" when expecting one of the following: )
, * & - + / at mod remainder rem and or ||
Could anyone help me with this?
As stated in the other answers the reason why your function won't compile is threefold.
You've declared the variable v_names and are referencing it as v_name.
The assignment operator in PL/SQL is :=, you're using the equality operator =.
You're missing a semi-colon in your return statement; it should be return v_name;
It won't stop the function from compiling but the variable v_names is declared as a varchar2(10). It's highly unlikely that when a manager with multiple subordinates all their last names will fit into this. You should probably declare this variable with the maximum size; just in case.
I would like to add that you're doing this a highly inefficient way. If you were to do the string aggregation in SQL as opposed to a PL/SQL loop it would be better. From 11g release 2 you have the listagg() function; if you're using a version prior to that there are plenty of other string aggregation techniques to achieve the same result.
create or replace function employee ( p_manid in employees.manager_id%type
) return varchar2 is
v_names varchar2(32767); -- Maximum size, just in case
begin
select listagg(lastname, ', ') within group ( order by lastname )
into v_names
from employees
where manager_id = p_manid;
return v_names;
exception when no_data_found then
return null;
end;
/
Please note a few other changes I've made:
Prepend a different letter onto the function parameter than the variable to make it clear which is which.
Add in some exception handling to deal with there being no data for that particular manager.
You would have returned , if you had no data I return NULL. If you want to return a comma instead simply put this inside the exception.
Rather than bother to create a cursor and loop through it etc I let Oracle do the heavy lifting.
It's rather curious that you would want to return a comma delimited list as there is little that you would be able to do with it in Oracle afterwards. It might be more normal to return something like an array or an open cursor containing all the surnames. I assume, in this answer, that you have a good reason for doing what you are.
There are a couple of things to be noted.
Declared as v_names but used as v_name
Assignemnt should be like v_name := v_name || emp_rec.last_name ||
', ';
v_name is declared with size of 10, it would be too small and would
give an error when you execute, so you could declare as
v_name employees.last_name%TYPE;
You could create your function as
CREATE OR REPLACE FUNCTION employee (v_manid IN employees.manager_id%TYPE)
RETURN VARCHAR2
AS
v_name employees.last_name%TYPE;
CURSOR cur_emp
IS
SELECT last_name
FROM employees
WHERE manager_id = v_manid;
BEGIN
FOR emp_rec IN cur_emp
LOOP
v_name := v_name || emp_rec.last_name || ', ';
END LOOP;
RETURN v_name;
END;
/
I guess you should use := instead of =
like
v_name := v_name || emp_rec.last_name ||', ';
one more thing you also need to add semicolon ; at the end of return v_name like
return v_name;