Pl/Sql Oracle Function in a procedure - sql

I need to make a procedure that needs to update the situation of a student, but I need to use a function that returns 'A' for the approved students, and 'R' for the not approved. After this I need to update the field "Situation" with an 'A' or 'R' that is returned from the function. I already have the function, but I don't have any idea how to make the procedure. Here goes the code of the function:
CREATE OR REPLACE FUNCTION check_grade(grade IN NUMBER,
frequency IN NUMBER) RETURN VARCHAR2 AS
RESULT VARCHAR2(1) DEFAULT '';
BEGIN
IF (grade >= 6) AND (frequency >= 0.75) THEN
resultado := 'A';
ELSE
resultado := 'R';
END IF;
RETURN RESULT;
END;

I think you are over complicating the things. You could do it in pure SQL rather than using PL/SQL function and procedure.
You could use CASE expression, for example:
Test 1
SQL> SELECT
2 CASE
3 WHEN &grade >=6
4 AND &frequency>=0.75
5 THEN 'A'
6 ELSE 'R'
7 END
8 FROM DUAL;
Enter value for grade: 10
old 3: WHEN &grade >=6
new 3: WHEN 10 >=6
Enter value for frequency: 1
old 4: AND &frequency>=0.75
new 4: AND 1>=0.75
C
-
A
Test 2
SQL> SELECT
2 CASE
3 WHEN &grade >=6
4 AND &frequency>=0.75
5 THEN 'A'
6 ELSE 'R'
7 END
8 FROM DUAL;
Enter value for grade: 1
old 3: WHEN &grade >=6
new 3: WHEN 1 >=6
Enter value for frequency: 0.5
old 4: AND &frequency>=0.75
new 4: AND 0.5>=0.75
C
-
R
So, using the same logic in your UPDATE statement:
UPDATE table_name
SET situation = (
CASE
WHEN &grade >=6
AND &frequency>=0.75
THEN 'A'
ELSE 'R'
END)
WHERE situation IS NOT NULL;
Update OP only wants to do it in a procedure:
create or replace procedure pr_update
is
begin
update table_name
set Situation = check_grade(grade, frequency);
end;
/
In the above procedure, put actual table_name in the update statement. grade and frequency are considered as the column names of the table.

See if this help.
create or replace procedure pr_update(v_grade in number, v_frequency in number) is
update tab_name set Situation=check_grade(v_grade,v_frequency);
end;

Related

How can I compare two string values in SQL?

The problem statement is as mentioned below:
When max_pos> 4 and max_neg is >1
then find out the name of rollercoaster
where intensity_level is greater than excitement_level.
Here, the intensity_level and excitement_level are string values like high, very high etc. How can I compare them?
You could use CASE expression to map value for your intensity_level and excitement_level, then compare them
SELECT *
FROM table_name
WHERE
max_pos> 4
AND max_neg >1
AND
(
CASE
WHEN intensity_level = 'high' THEN 0
WHEN intensity_level = 'very high' THEN 1
WHEN intensity_level = 'ultra high' THEN 2
END
>
CASE
WHEN excitement_level = 'high' THEN 0
WHEN excitement_level = 'very high' THEN 1
WHEN excitement_level = 'ultra high' THEN 2
END
);
You can create a table that contains intensity_level and excitement_level for each rollercoaster as integers and an ID that links them to your main table
try using DECODE. DECODE high, very high values with a number and alias and then select all from this result set, then compare the aliases
Here My solution using the function
Just assign a integer value to the string and then compare the integer value.
CREATE function Weight(tag VARCHAR(200))
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE v INT;
IF tag='High' THEN
SET v=1;
END IF;
IF tag='Very High' THEN
SET v=2;
END IF;
RETURN v;
END;|
Then use the query
Select *
From tablename
Where max_pos> 4
AND max_neg is >1
AND Weight(intensity_level)>Weight(excitement_level);
The Weight function above maybe not work in SQL, I used MySQL so change accordingly.

Need help for dynamic calculation in oracle sql query

I Have a table tab_1 with below values.
ID Calculation value
1 10
2 10
3 1+2
4 5
5 3-2
6 5+1
Need help writing the query for following logic. I have a table where the records contain either calculation strings or values to be used in calculations. I need to parse the calculation like this:
ID 3 is the sum of ID 1 and 2.
ID 5 is the minus of ID 3 and 2.
ID 6 is the sum of ID 5 and 1.
Then I need to select the records for the referenced IDs and perform the calculations. My
expected output:
ID Calculation value
3 1+2 20
5 3-2 10
6 5+1 20
Thanks -- nani
"Need help writing the query for below logic."
This is not a problem which can be solved in pure SQL, because:
executing the calculation string requires dynamic SQL
you need recursion to look up records and evaluate the results
Here is a recursive function which produces the answers you expect. It has three private procs so that the main body of the function is simple to understand. In pseudo-code:
look up record
if record is value then return it and exit
else explode calculation
recurse 1, 3, 4 for each part of exploded calculation until 2
Apologies for the need to scroll:
create or replace function dyn_calc
(p_id in number)
return number
is
result number;
n1 number;
n2 number;
l_rec t23%rowtype;
l_val number;
type split_calc_r is record (
val1 number
, operator varchar2(1)
, val2 number
);
l_calc_rec split_calc_r;
function get_rec
(p_id in number)
return t23%rowtype
is
rv t23%rowtype;
begin
select *
into rv
from t23
where id = p_id;
return rv;
end get_rec;
procedure split_calc
(p_calc in varchar2
, p_n1 out number
, p_n2 out number
, p_operator out varchar2)
is
begin
p_n1 := regexp_substr(p_calc, '[0-9]+', 1, 1);
p_n2 := regexp_substr(p_calc, '[0-9]+', 1, 2);
p_operator := translate(p_calc, '-+*%01923456789','-+*%'); --regexp_substr(p_calc, '[\-\+\*\%]', 1, 1);
end split_calc;
function exec_calc
(p_n1 in number
, p_n2 in number
, p_operator in varchar2)
return number
is
rv number;
begin
execute immediate
'select :n1 ' || p_operator || ' :n2 from dual'
into rv
using p_n1, p_n2;
return rv;
end exec_calc;
begin
l_rec := get_rec(p_id);
if l_rec.value is not null then
result := l_rec.value;
else
split_calc(l_rec.calculation
, l_calc_rec.val1
, l_calc_rec.val2
, l_calc_rec.operator);
n1 := dyn_calc (l_calc_rec.val1);
n2 := dyn_calc (l_calc_rec.val2);
result := exec_calc(n1, n2, l_calc_rec.operator);
end if;
return result;
end;
/
Run like this:
SQL> select dyn_calc(6) from dual;
DYN_CALC(6)
-----------
20
SQL>
or, to get the output exactly as you require:
select id, calculation, dyn_calc(id) as value
from t23
where calculation is not null;
Notes
There is no exception handling. If the data is invalid the function will just blow up
the split_calc() proc uses translate() to extract the operator rather than regex. This is because regexp_substr(p_calc, '[\-\+\*\%]', 1, 1) mysteriously swallows the -. This appears to be an environment-related bug. Consequently extending this function to process 1+4+2 will be awkward.
Here is a LiveSQL demo.
In SQL:
select 'ID ' +ID+ ' is the ' + case when calculation like '%-%' then ' minus '
when calculation like '%+%' then ' sum ' END +' of
ID'+replace(replace(calculation,'+',' and '),'-',' and ')
from tab_1
where calculation is not null
In Oracle:
select 'ID ' ||ID|| ' is the ' || case when calculation like '%-%' then ' minus '
when calculation like '%+%' then ' sum ' END|| ' of
ID'||replace(replace(calculation,'+',' and '),'-',' and ')
from tab_1
where calculation is not null

IF-ELSE statement: create a column depending on another one

In the table JAN07, I have a column TEMPO_INTERCORSO (number(10,0)) and I want to create another column ANTE_POST (number(1)) that is '0' when TEMPO_INTERCORSO > 0 and '1' otherwise.
IF (TEMPO_INTERCORSO > 0) THEN
UPDATE JAN07 SET ANTE_POST = 1
ELSE
UPDATE JAN07 SET ANTE_POST = 0
END IF;
I've tried a lot of different ways that you can find on the web (for example the ";" before the ELSE: sometimes there is, sometimes not), but it is still not working.
IF (TEMPO_INTERCORSO = 0) THEN
Report error -
Comando sconosciuto
Some ideas?
ps= I've tried with JAN07.TEMPO_INTERCORSO too!
The following UPDATE query uses CASE...WHEN to achieve what you want:
UPDATE JAN07
SET ANTE_POST = CASE WHEN TEMPO_INTERCORSO > 0 THEN 1 ELSE 0 END
I would rather suggest Virtual Columns introduced in Oracle Database 11g Release 1. A simple CASE statement would do the rest.
For example,
SQL> CREATE TABLE t
2 (
3 TEMPO_INTERCORSO NUMBER,
4 ANTE_POST NUMBER GENERATED ALWAYS AS (
5 CASE
6 WHEN TEMPO_INTERCORSO > 0
7 THEN 1
8 ELSE 0
9 END) VIRTUAL
10 );
Table created.
Now, you need not worry about manually updating the virtual column, it will be automatically generated at run time.
Let's insert the values only in static column and see:
SQL> INSERT INTO t(TEMPO_INTERCORSO) VALUES(0);
1 row created.
SQL> INSERT INTO t(TEMPO_INTERCORSO) VALUES(1);
1 row created.
SQL> INSERT INTO t(TEMPO_INTERCORSO) VALUES(10);
1 row created.
SQL> commit;
Commit complete.
SQL> SELECT * FROM t;
TEMPO_INTERCORSO ANTE_POST
---------------- ----------
0 0
1 1
10 1
So, you have your column ANTE_POST with desired values automatically.
Conditional control IF-THEN-ELSE STATEMENT is one statement in PL/SQL. Each statement in PL/SQL ends with semi color. Hence it's written like this:
IF condition THEN
{...statements to execute when condition is TRUE...}
ELSE
{...statements to execute when condition is FALSE...}
END IF; --semi colon at the end
In your code, there is an update statement inside. Therefore, at the end of this statement there must be semi colon. Your code can be written like this:
IF (TEMPO_INTERCORSO > 0) THEN
UPDATE JAN07 SET ANTE_POST = 1; --semi colon
ELSE
UPDATE JAN07 SET ANTE_POST = 0; --semi colon
END IF; --semi colon
Some answer already suggested doing that in pure SQL rather than PL/SQL. Which I agree with.

Simple update set postgres stored procedure

I've a problem trying to make my stored procedure work.
This is my problem:
I have a table with a columns called a, in this column there are telephone numbers.
I have to add 0039 if the number starts with 1,8,3 or 0 (or leave it as is if not) and store the new number in the column b.
This is my code:
CREATE OR REPLACE FUNCTION upg_table() RETURNS void AS $$
BEGIN
IF (substring(a from 0 for 2)!='00')
AND (substring( a from 0 for 1)='3')
OR (substring(a from 0 for 1)='0')
OR (substring(a from 0 for 1)='1')
OR ( substring(a from 0 for 1)='8')
THEN
UPDATE cdr
SET
b = '0039'||a;
ELSE
UPDATE cdr
SET
b = a;
END IF;
END;
$$ LANGUAGE plpgsql;
The error is:
ERROR: the column "a" does not exist
ROW 1: SELECT substring(a from 0 for 2)!='00' AND ...
You code has two errors:
You cannot reference the column a like it was a (non-existent) plpgsql variable. You would have to loop, too. Your approach does not work at all.
You got operator precedence wrong. AND binds before OR.
But most importantly, you don't need a plpgsql function. A plain UPDATE will do the job:
UPDATE cdr
SET b = CASE WHEN left(a, 1) IN ('0', '1', '3', '8')
AND left(a, 2) <> '00'
THEN '0039' || a ELSE a END;
This updates b in all rows, but only some with a changed a.

Oracle CASE expression documentation issue

In the SQL Language Reference for Oracle 11g R2, the documentation for the simple CASE expressions says that:
You cannot specify the literal NULL for every return_expr and the else_expr.
However the following SQL executes without problem and returns null:
select case 'test'
when 'test' then null
else null
end "Null Test"
from dual;
Is this a problem with the documentation or am I missing something?
You have to have at least one not null return expression when your case expression(whether it's simple or search case expression) is used inside a PL/SQL block. In SQL this restriction is relaxed:
Is this a problem with the documentation or am I missing something
It seems like a minor documentation bug.
SQL:
SQL> select case 1
2 when 1 then null
3 else null
4 end as res
5 from dual
6 ;
Result:
RES
---
null
PL/SQL:
SQL> declare
2 l_res number;
3 begin
4 l_res := case 1
5 when 1 then null
6 else null
7 end;
8 end;
9 /
ORA-06550: line 4, column 11:
PLS-00617: at least one result in the CASE expression must not be NULL
ORA-06550: line 4, column 2:
PL/SQL: Statement ignored