PLSQL invalid number error after fetch - plsql

sorry if this is an confusing post and the style is not proper but this is my first question but im having an error after i want to fetch c_latlong :
the things he will fetch are:
LAT value : 50,9459744669855
LON value : 5,9704909091251
ORA-01722: invalid number
ORA-06512: at "DOMINOS.GETLATLONG", line 14
ORA-06512: at "DOMINOS.ZOEKWINKELVOORADRES2", line 34
ORA-06512: at line 9
This is the code i use:
create or replace FUNCTION getLatLong(v_postcode varchar2, v_huisnummer varchar2)
RETURN varchar2
IS
v_lat NVARCHAR2(38) ;
v_long NVARCHAR2(38);
v_postcode_id varchar2(200);
v_all varchar2(200);
CURSOR c_latlong IS
SELECT LAT, LON
FROM POSTCODE
WHERE POSTCODE = v_postcode AND v_huisnummer BETWEEN MINNUMBER AND MAXNUMBER;
BEGIN
OPEN c_latlong;
FETCH c_latlong INTO v_lat, v_long;
v_all := v_lat || '-' || v_long;
-- print(v_all);
RETURN v_all;
CLOSE c_latlong;
END getLatLong;
create or replace FUNCTION zoekWinkelVoorAdres2(v_postcode varchar2, v_huisnummer varchar2)
RETURN varchar2
IS
v_array_this apex_application_global.vc_arr2 := apex_util.string_to_table(getLatLong(v_postcode,v_huisnummer), '-');
v_temp_postcode varchar2(200);
v_temp_huisnummer varchar2(200);
v_temp_id varchar2(200);
v_temp_winkel apex_application_global.vc_arr2;
v_array dbms_sql.varchar2_table;
v_max number;
v_closest_winkel varchar2(200);
v_closest_dis varchar2(200);
CURSOR c_all_stores_max IS
SELECT count(*)
FROM WINKEL;
CURSOR c_all_stores IS
SELECT ID, POSTCODE, HUISNR
FROM WINKEL;
CURSOR c_select_dis IS
SELECT WINKEL_ID, DISTANCE
FROM allwinkeldis ORDER BY DISTANCE DESC;
BEGIN
--DELETE FROM allwinkeldis;
OPEN c_all_stores_max;
FETCH c_all_stores_max INTO v_max;
CLOSE c_all_stores_max;
OPEN c_all_stores;
FOR i IN 1..v_max
LOOP
FETCH c_all_stores INTO v_temp_id, v_temp_postcode, v_temp_huisnummer;
-- print('-------------------1---------------------' ||v_temp_postcode);
print(getLatLong(v_temp_postcode,v_temp_huisnummer));
v_temp_winkel :=apex_util.string_to_table(getLatLong(v_temp_postcode,v_temp_huisnummer), '-');
-- print(v_temp_id || ' ' || v_temp_winkel(1) || ' ' || v_temp_winkel(2));
-- print('-------------------2---------------------');
--v_array(i) := v_temp_id ||','||distance(v_array_this(2), v_array_this(3),v_temp_winkel(2), v_temp_winkel(3));
INSERT INTO allwinkeldis(winkel_id, distance) VALUES (v_temp_id, distance(v_array_this(1), v_array_this(2), v_temp_winkel(1), v_temp_winkel(2)));
--print(v_temp_id ||' -------------- '||distance(v_array_this(2), v_array_this(3), v_temp_winkel(2),v_temp_winkel(3)));
END LOOP;
CLOSE c_all_stores;
OPEN c_select_dis;
FETCH c_select_dis INTO v_closest_winkel, v_closest_dis;
CLOSE c_select_dis;
print(v_closest_winkel || '--------------' || v_closest_dis);
END zoekWinkelVoorAdres2;

I'm not sure what is the data type of LAT and LON in your table POSTCODE.
But whatever variables you define to fetch data into, they must match these types.
For examples, assuming that LAT and LON are of the type NVARCHAR2, then I would rewrite your declaration section as follows:
CREATE OR REPLACE FUNCTION getLatLong(v_postcode varchar2, v_huisnummer varchar2)
RETURN varchar2
IS
v_postcode_id varchar2(200);
v_all varchar2(200);
CURSOR c_latlong IS
SELECT LAT, LON
FROM POSTCODE
WHERE POSTCODE = v_postcode AND v_huisnummer BETWEEN MINNUMBER AND MAXNUMBER;
TYPE temp_nvarchar2 IS TABLE OF NVARCHAR2(38);
v_lat temp_nvarchar2;
v_long temp_nvarchar2;
BEGIN
....

The invalid number error occurs when it expects a number and gets something else instead, like a varchar.
v_lat and v_lon are declared as NVARCHAR2. Is this the same datatype that is stored in POSTCODE? If not, this is the cause of the error.
It might also be having trouble with the line v_all := v_lat || '-' || v_long;. If v_lat or v_long are numbers, then you need to cast them to chars instead, like so:
v_all := TO_CHAR(v_lat) || '-' || TO_CHAR(v_long);

Related

How to use for loop in sql developer or oracle

This is my code but while I'm running the project, it's showing error like
PLS-00306: wrong number or types of arguments in call to 'TEST_PROCEDURE'
I want to insert multiple records according given date.
CREATE OR REPLACE PROCEDURE TEST_PROCEDURE (
IP_START_DATE IN VARCHAR2,
IP_END_DATE IN VARCHAR2,
IP_MATERIAL_TYPE IN VARCHAR2,
IP_BRM_LIST IN VARCHAR2,
IP_ACTUAL_CONSUMPTION IN VARCHAR2,
IP_YARD_NO IN VARCHAR2,
IP_USER_ID IN VARCHAR2,
IP_USER_IP IN VARCHAR2,
IP_RID IN NUMBER,
IP_OPERATION IN VARCHAR2,
i OUT VARCHAR2,
OUT_RETURN_MSG OUT VARCHAR2,
OUT_RETURN_CODE OUT NUMBER)
IS
BEGIN
OUT_RETURN_MSG := '';
OUT_RETURN_CODE := 0;
BEGIN
IF IP_OPERATION = 'INSERT'
THEN
FOR i IN IP_START_DATE .. IP_END_DATE
LOOP
IF i <= IP_END_DATE
THEN
-- exit loop immediately
INSERT INTO MST_ACTUAL_CONSUMPTION (FROM_DATE,
TO_DATE,
MATERIAL_TYPE,
BRM_TYPE,
ACTUAL_CONSUMPTION,
YARD_NO,
USER_ID,
USER_IP_ADDRESS,
CREATED_DATE)
VALUES (IP_START_DATE,
IP_END_DATE,
IP_MATERIAL_TYPE,
IP_BRM_LIST,
IP_ACTUAL_CONSUMPTION,
IP_YARD_NO,
IP_USER_ID,
IP_USER_IP,
SYSDATE);
EXIT;
END IF;
END LOOP;
END IF;
IF IP_OPERATION = 'UPDATE'
THEN
UPDATE MST_ACTUAL_CONSUMPTION
SET FROM_DATE = IP_START_DATE,
TO_DATE = IP_END_DATE,
MATERIAL_TYPE = IP_MATERIAL_TYPE,
BRM_TYPE = IP_BRM_LIST,
ACTUAL_CONSUMPTION = IP_ACTUAL_CONSUMPTION,
YARD_NO = IP_YARD_NO,
LAST_UPD_TS = SYSDATE,
LAST_UPD_UID = IP_USER_ID
WHERE RID = IP_RID;
-- AND VESSEL_NAME = IP_VESSEL_NAME;
END IF;
OUT_RETURN_CODE := 1;
OUT_RETURN_MSG := 'SUCCESS';
EXCEPTION
WHEN OTHERS
THEN
OUT_RETURN_CODE := 0;
OUT_RETURN_MSG := SQLERRM;
END;
END TEST_PROCEDURE;
You need to select the days between start and end and use that for your loop counter instead of trying to use the variables directly as you currently are. Even though you're passing varchar2 with dates inside them, I still trunc the casted variable for safety to guarantee a whole number.
select trunc(to_date(ip_end_date)) - trunc(to_date(ip_start_date))
into loop_count
from dual;
Change your loop to count by this
if loop_count > 0 then
FOR i IN 1..loop_count loop
and I'm assuming you probably want to change the date for each iteration of the loop to match the day between start and end.
Firstly, you shouldn't be using varchar2 datatype for date datatypes. That is asking for trouble.
Now, since you want to insert records for multiple dates, you can't use for loop iterator for dates the way you do it for numbers as in for i in 1..10.
So, you could use a connect by as a method to generate dates. Use LEVEL pseudocolumn to refer to the current iteration (i in the for loop)
INSERT INTO mst_actual_consumption (
from_date,
TO_DATE, -- don't use this as a column_name as it's a function
material_type,
brm_type,
actual_consumption,
yard_no,
user_id,
user_ip_address,
created_date
)
SELECT
ip_start_date, -- you need to use to_date if you are passing date as strings
ip_end_date,
ip_material_type,
ip_brm_list,
ip_actual_consumption,
ip_yard_no,
ip_user_id,
ip_user_ip,
SYSDATE
FROM
dual
CONNECT BY
level <= ( ip_end_date - ip_start_date ); -- you need to use to_date if you are passing date as strings
I did not understand this part though
IF i <= IP_END_DATE
THEN
-- exit loop immediately
Maybe you were trying this? If so, put it before insert and update
IF ip_end_date <= ip_start_date
THEN
RETURN;
END IF;
EDIT you said:
error has fixed but no records are inserting even one time also
It could be because you are not passing correct dates.As I said in the code comments, you should convert them to dates using to_date function.
For eg: if you are passing the dates as strings in the format dd-mm-yyyy, In the insert, you should use
level <= ( to_date(ip_end_date,'dd-mm-yyyy') - to_date(ip_start_date,'dd-mm-yyyy') )
Same thing has to be done in the select part of insert, if the column in table MST_ACTUAL_CONSUMPTION is of date datatype.
select to_date(ip_start_date,'dd-mm-yyyy'), to_date(ip_end_date,'dd-mm-yyyy')
you can't loop through dates, you can probably do something like this.
create or replace procedure testproc(
startdate in date,
enddate in date
)
as
datediff NUMBER := enddate-startdate;
newdate date := startdate;
begin
FOR i IN 1..datediff LOOP
newdate := newdate+1;
insert into temp values(newdate);
END LOOP;
end;
and for your error it's because you are passing your parameters incorrectly like passing a string where argument requires integer value or you are passing incorrect number of parameters.
create or replace PROCEDURE PROC_ADD_ACTUALCONSUMPTION (
IP_START_DATE IN VARCHAR2,
IP_END_DATE IN VARCHAR2,
IP_MATERIAL_TYPE IN VARCHAR2,
IP_BRM_LIST IN VARCHAR2,
IP_ACTUAL_CONSUMPTION IN VARCHAR2,
IP_YARD_NO IN VARCHAR2,
IP_USER_ID IN VARCHAR2,
IP_USER_IP IN VARCHAR2,
IP_RID IN NUMBER ,
IP_OPERATION IN VARCHAR2,
--S_DATE OUT DATE,
--E_DATE OUT DATE,
--datediff OUT NUMBER,
OUT_RETURN_MSG OUT VARCHAR2,
OUT_RETURN_CODE OUT NUMBER)
IS
BEGIN
OUT_RETURN_MSG := '';
OUT_RETURN_CODE := 0;
-- S_DATE :=TO_DATE(IP_START_DATE,'DD-MM-YYYY HH24:MI:SS');
-- E_DATE :=TO_DATE(IP_END_DATE,'DD-MM-YYYY HH24:MI:SS');
-- datediff :=E_DATE-E_DATE;
BEGIN
IF IP_OPERATION = 'INSERT' THEN
INSERT INTO MST_ACTUAL_CONSUMPTION
(
FROM_DATE,
TO_DATE,
MATERIAL_TYPE,
BRM_TYPE,
ACTUAL_CONSUMPTION,
YARD_NO,
USER_ID,
USER_IP_ADDRESS,
CREATED_DATE
)
select
TO_CHAR((to_date(IP_START_DATE,'DD-MM-YYYY HH24:MI:SS')+ (level-1)),'DD-MM-YYYY') ,
TO_CHAR(to_date(IP_END_DATE,'DD-MM-YYYY HH24:MI:SS'),'DD-MM-YYYY') ,
IP_MATERIAL_TYPE,
IP_BRM_LIST,
IP_ACTUAL_CONSUMPTION,
IP_YARD_NO,
IP_USER_ID,
IP_USER_IP,
SYSDATE
FROM
dual
CONNECT BY
level <= to_date(IP_END_DATE,'DD-MM-YYYY HH24:MI:SS')-to_date(IP_START_DATE,'DD-MM-YYYY HH24:MI:SS')+1;
-- to_date(IP_START_DATE,'DD-MM-YYYY HH24:MI:SS'):=to_date(IP_START_DATE,'DD-MM-YYYY HH24:MI:SS')+1;
END IF;
END PROC_ADD_ACTUALCONSUMPTION;

error in executing ODCIIndexStart() routine

I have a SEARCH package with several functions within that each return a pipelined table.
I am getting the following error:
ORA-29902: error in executing ODCIIndexStart() routine
ORA-06512: at "<SCHEMA_NAME>.SEARCH", line 955
ORA-06512: at "<SCHEMA_NAME>.SEARCH", line 216
ORA-06512: at line 1
ORA-06512: at "<SCHEMA_NAME>.SEARCH", line 1143
ORA-06512: at line 1
29902. 00000 - "error in executing ODCIIndexStart() routine"
*Cause: The execution of ODCIIndexStart routine caused an error.
*Action: Examine the error messages produced by the indextype code and
take appropriate action.
The command that I use to get this error is:
select * from table(search.user_search(10000003, '', 1, 20, 5, '', '', ''));
Here is the code around line 1143 of the SEARCH body:
FUNCTION USER_SEARCH(p_user_id number, p_search varchar2, p_page number, p_num_results number, p_concept_type_id number := null, p_standard_org varchar2 := null, p_standard_title varchar2 := null, p_standard_number varchar2 := null) RETURN SEARCH_RESULTS_TABLE AS
v_sub char(1);
v_rtn search_results_table := search_results_table();
v_user_id number;
v_page number;
v_num_results number;
v_query varchar2(4000);
cursor c_concept is
select * from table(search.concept_new(v_user_id, v_query, v_page, v_num_results, p_concept_type_id, p_standard_org, p_standard_title, p_standard_number));
BEGIN
v_page := p_page;
v_num_results := p_num_results;
v_query := p_search;
if (v_query = '') then
v_query := null;
end if;
-- ONLY DURING DEVELOPMENT
v_user_id := p_user_id;
if v_user_id = 0 then
v_user_id := 2;
end if;
if v_page = null then
v_page := 1;
end if;
if v_num_results = null then
v_num_results := -1;
end if;
select security.has_permission(p_user_id, 'UNLIMITED_RESULTS') into v_sub from dual;
case v_sub
when 'N' then
v_rtn := search.term(v_user_id, p_search, 1, 20, P_concept_type_id, p_standard_org, p_standard_title, p_standard_number);
when 'Y' then
for r_concept in c_concept loop -- line 1143
v_rtn.extend;
v_rtn(v_rtn.last) := search_results_row(r_concept.SCORE, r_concept.FROM_TABLE, r_concept.ID, r_concept.IN_DICTIONARY);
end loop;
end case;
return v_rtn;
END USER_SEARCH;
The code around line 216:
FUNCTION CONCEPT_NEW(p_user_id number, p_search varchar2, p_page number := 1, p_num_results number := -1, p_concept_type_id number := null, p_standard_org varchar2 := null, p_standard_title varchar2 := null, p_standard_number varchar2 := null) RETURN SEARCH_RESULTS_TABLE PIPELINED AS
out_rec search_results_row := search_results_row(null,null,null,null);
v_num_pages number;
v_num_results number;
cursor c_count is
select count(*) cnt, ceil(count(*) / p_num_results) num_pages from (
select * from (
select rownum rn, bb.* from (
select * from (
select sum(score) score, concept_id from
(
(
select
sum(score) score,
concept_id
from
(
select
s.score + (case
when c.eotd = 'N' then
200
else
0
end) score,
ct.concept_id
from
table(search.synonym_search(p_search)) s,
term_synonym ts,
concept_term ct,
concept c
where
c.eotd = 'N' and
s.table_id = ts.synonym_id and
ts.term_id = ct.term_id and
c.concept_id = ct.concept_id
)
group by
concept_id
)
union all
(
select
sum(score) score,
concept_id
from
(
select
s.score + (case
when c.eotd = 'N' then
200
else
0
end) score,
cd.concept_id
from
table(search.definition_search(p_search)) s,
concept_definition cd,
concept c
where
c.eotd = 'N' and
s.table_id = cd.definition_id and
c.concept_id = cd.concept_id
)
group by
concept_id
)
)
group by concept_id
) order by score desc
) bb) where score > 100) where rn < 1000;
-- another cursor here called c_search (removed for cleanliness)
v_added boolean;
v_in_dict varchar(1);
v_concept_id number;
cursor c_in_dict is
select * from dictionary_concept where concept_id = v_concept_id and dictionary_id in (select dictionary_id from company_dictionary where client_company_id in (select client_company_id from users where users_id = p_user_id));
v_in_dict_result c_in_dict%rowtype;
BEGIN
open c_count;
fetch c_count into v_num_results, v_num_pages; -- line 216
close c_count;
out_rec.SCORE := v_num_results;
out_rec.FROM_TABLE := 'HEADER';
out_rec.ID := v_num_pages;
out_rec.IN_DICTIONARY := 'N';
pipe row(out_rec);
-- more code here but been removed
RETURN;
END CONCEPT_NEW;
The code around line 995:
FUNCTION STANDARD_SEARCH(p_search varchar2) RETURN RESULT_TABLE
PIPELINED
AS
out_rec result_type := result_type(null, null);
cursor c_search is
select
45 score,
source_id
from
source
where
source_title = p_search or
standard_number = p_search
union all
select
40,
source_id
from
source
where
lower(source_title) = lower(p_search) or
lower(standard_number) = lower(p_search)
union all
select
35,
source_id
from
source
where
lower(source_title) like lower(p_search) || '%' or
lower(standard_number) like lower(p_search) || '%'
union all
select
30,
source_id
from
source
where
lower(source_title) like '%' || lower(p_search) || '%' or
lower(standard_number) like '%' || lower(p_search) || '%'
union all
select
10 * contains(source_title, lower(p_search)),
source_id
from
source
where
contains(source_title, lower(p_search)) > 1
union all
select
10 * contains(standard_number, lower(p_search)),
source_id
from
source
where
contains(standard_number, lower(p_search)) > 1;
BEGIN
for r_search in c_search loop -- line 995
out_rec.SCORE := r_search.SCORE;
out_rec.TABLE_ID := r_search.SOURCE_ID;
pipe row(out_rec);
end loop;
return;
END;
Change = null to is null. Zero-length strings in Oracle are already NULL so = '' is also incorrect.
Those are some obvious bugs but the code is so complex I'm not sure if they are directly related to the errors. You may need to shrink your code down until it can fit inside a small, reproducible test case. That can take hours of work sometimes.
Another suggestion is to rethink whether you need to use pipelined functions. In my experience pipelined functions are buggy and over-used. Sometimes they can be replaced by regular SQL statements. Sometimes they can be replaced by a simpler function that returns a complete collection, instead of returning it one row at a time. Unless the collection is so ginormous that it can't reasonably fit in memory, or you need the function to return the first N results so they can be processed immediately, the pipelined feature doesn't help.

Using parameters in procedures

I need to create a procedure that receives as a parameter a month and a year.
Inside the procedure I need to have a query to retrieve some values, that will have to take in account the parameters received.
create or replace procedure GET_REVS(MONTH in VARCHAR2,YEAR in varchar2) is
SELECT
*
FROM
REVENUS_TABLE
WHERE
Y_CODE IN ('YEAR')
AND M_CODE IN ()
Now, M_CODE should have the values since the start of the year until the month received by parameter.
Example if i receive in as parameter for the month a 4 i want my select to like this AND M_CODE IN ('1','2','3','4')
But if i receive MONTH = 3.. i need the select to have AND M_CODE IN ('1','2','3')
So what is the best way to do the procedure in order to be able to do that?
Thanks a lot
You could cast both M_CODE and MONTH as numbers and use BETWEEN operator or just <=:
CREATE OR REPLACE PROCEDURE get_revs(month IN VARCHAR2, year IN VARCHAR2) IS
BEGIN
....
SELECT *
FROM revenus_table
WHERE y_code = year
AND TO_NUMBER(m_code) BETWEEN 1 AND TO_NUMBER(month);
...
END;
You can do some thing like below example to make your procedure more dynamic
CREATE OR REPLACE PROCEDURE get_revs (
MONTH IN VARCHAR2,
YEAR IN VARCHAR2
)
IS
variable_name table_name%ROWTYPE;
v_sql VARCHAR2 (1000)
:= 'SELECT * FROM REVENUS_TABLE WHERE Y_CODE IN ('
|| MONTH
|| ')';
BEGIN
IF MONTH = 4 THEN
v_sql := v_sql || ' and M_CODE IN (''1'',''2'',''3'',''4'')';
END IF;
IF MONTH = 3 THEN
v_sql := v_sql || 'and M_CODE IN (''1'',''2'',''3'')';
END IF;
EXECUTE IMMEDIATE v_sql
INTO variable_name;
END;
Just an alternative to think about.
CREATE OR REPLACE PROCEDURE get_revs(month IN VARCHAR2, year IN VARCHAR2) IS
lv_in_clause VARCHAR2(100 CHAR);
p_ref sys_refcursor;
BEGIN
SELECT 'IN ('
||WMSYS.WM_CONCAT(A.NUM)
||')'
INTO lv_in_clause
FROM
(SELECT ''''
||LEVEL
||'''' NUM,
1 ID1
FROM DUAL
CONNECT BY LEVEL < to_number(MONTH)
)A
GROUP BY a.ID1;
OPEN p_ref FOR 'SELECT *
FROM revenus_table
WHERE y_code = '|| year
||' AND m_code '||lv_in_clause;
END;

Passsing two VARRAYS to a Procedure to pass them to a Index-By table

I try to pass two VARRAYs into a Procedure but when call it to test it I don't get any kind of respond from my database.
CREATE OR REPLACE PACKAGE PKG_TEST AS
TYPE PNO IS VARRAY(20) OF VARCHAR(20);
TYPE QTY IS VARRAY(20) OF INTEGER;
TYPE indexTest IS TABLE OF NUMBER INDEX BY VARCHAR2(20);
PROCEDURE blatest(i_PNO IN PNO, i_QTY IN QTY);
END PKG_TEST;
/
CREATE OR REPLACE PACKAGE BODY PKG_TEST AS
PROCEDURE blatest(i_PNO IN PNO , i_QTY IN QTY)
IS
V_COUNT_PNO INTEGER;
V_COUNT_QTY INTEGER;
bla_list indexTest;
name VARCHAR(20);
BEGIN
V_COUNT_PNO := i_PNO.COUNT;
V_COUNT_QTY := i_QTY.COUNT;
IF V_COUNT_PNO = V_COUNT_QTY THEN
FOR I IN 1..V_COUNT_PNO LOOP
bla_list(i_PNO(I)) := i_QTY(I);
END LOOP;
name := bla_list.FIRST;
WHILE name IS NOT null LOOP
dbms_output.put_line('Name: ' || name || ' is ' || TO_CHAR(bla_list(name)));
name := bla_list.NEXT(name);
END LOOP;
ELSE
dbms_output.put_line('Amount of Variables is not identical!');
END IF;
END blatest;
END PKG_TEST;
/
PKG_TEST.blatest(PKG_TEST.PNO('P123','P124'), PKG_TEST.QTY(2,3));
/
And if there is any easier way to fill in a Index-By table I dynamicly I am more than happy to read this ^^. Thanks in advance!
Call it as below, then you will get response:
set serveroutput on;
BEGIN
pkg_test.blatest (pkg_test.pno ('P123', 'P124'), pkg_test.qty (2, 3));
END;

PLSQL, execure formula stored in a table

I'm a newbie in PLSQL. I was just wondering if I can save my formula into a table as string and use it in my functions to calculate some values.
Here is an example:
ID NAME FORMULA
1 test prm_testval*prm_percent/18
2 test2 (prm_testval +20)*prm_percent
what I want to do is to select formula column from the table and use the string in my functions
select t.* from table t where id=1
prm_calculated_value = t.formula
I don't want the string value of formula in here, just the formula itself.
Any ideas, If I can use it or not?
The starting point is execute immediate-statement. It's PL/SQL's eval().
create table formulas (
id number,
name varchar2(20),
formula varchar2(50)
);
insert into formulas values (1, 'test 1', 'prm_testval*prm_percent/18');
insert into formulas values (2, 'test 2', '(prm_testval +20)*prm_percent');
/* eval from: http://www.adp-gmbh.ch/blog/2005/may/5.html */
create or replace function eval (
expr in varchar2
) return varchar2 as
ret varchar2(32767);
begin
execute immediate 'begin :result := ' || expr || '; end;' using out ret;
return ret;
end;
/
create or replace function eval2 (
vars in varchar2,
expr in varchar2
) return varchar2 as
ret varchar2(32767);
begin
execute immediate vars || ' begin :result := ' || expr || '; end;' using out ret;
return ret;
end;
/
create or replace function calc_prm (
id_ in number,
prm_testval in varchar2,
prm_percent in varchar2
) return number as
formula_ formulas.formula%type;
vars constant varchar2(32767) :=
'declare prm_testval constant number := to_number(' || prm_testval ||
'); prm_percent constant number := to_number(' || prm_percent || ');';
begin
select formula into formula_ from formulas where id = id_;
return eval2(vars, formula_);
end;
/
/* call from SQL */
select eval('3*4') from dual;
select calc_prm(1, 97, 10) from dual;
select calc_prm(2, 97, 10) from dual;
/* call from PL/SQL block */
begin
dbms_output.put_line(eval('3*4'));
dbms_output.put_line(calc_prm(1, 97, 10));
dbms_output.put_line(calc_prm(2, 97, 10));
end;
/
Based on this example you can start building your own way to map symbol values (prm_testval and prm_percent) to real values. Next you might want to have a look into DBMS_SQL.
Beware SQL injection when using client supplied data ! See also e.g. Bobby Tables: A guide to preventing SQL injection.

Resources