I have a problem with new charts in Oracle APEX 20.1.
I need to do chart from PL/SQL Function Body returning SQL Query,
but it only works if I do it with some exact parameters. All variables return from the same submitted page.
If I use all variables as parameter then i get error: ORA-20999: PL/SQL function body did not return a value.
If I use variable :P2_OBJECT and parameter for :P2_YEAR and :P2_ANNUAL_TIME then i get error: ORA-20999: PL/SQL function body did not return a value.
If I use variable :P2_YEAR and parameter for :P2_OBJECT and :P2_ANNUAL_TIME then i get error: ORA-20999: Parsing returned query results in "ORA-20999: Failed to parse SQL query! ORA-06550: line 2, column 402: ORA-00936: missing expression".
If I use variable :P2_ANNUAL_TIME and parameter for :P2_OBJECT and :P2_YEAR then i get error: ORA-20999: Parsing returned query results in "ORA-20999: Failed to parse SQL query! ORA-06550: line 2, column 113: ORA-01741: illegal zero-length identifier".
but in a classic report the same function with variables works just fine...
this source for chart works:
declare l_sql varchar2(2000) ;
begin
select F_CHARTS(4020 , 2018 ,'TIM_MM',
:P2_LEVELS, :P2_SUB_LEVELS, :P2_SQL_CONDITION, :P2_WAREHOUSE, :P2_UNIT_OF_MEASURE)
into l_sql from dual;
return l_sql ;
end ;
but this does not:
declare
l_sql varchar2(2000) ;
begin
select F_CHARTS(:P2_OBJECT,:P2_YEAR,:P2_ANNUAL_TIME,
:P2_LEVELS, :P2_SUB_LEVELS, :P2_SQL_CONDITION, :P2_WAREHOUSE, :P2_UNIT_OF_MEASURE)
into l_sql from dual;
return l_sql ;
end ;
This is the function:
create or replace FUNCTION F_CHARTS(
cod in NUMBER,
year in NUMBER,
t_time in varchar2,
v_dims in varchar2,
p_subl in varchar2,
cod_filter in NUMBER,
w_warehouse in varchar2,
amount in NUMBER)
return varchar2 is s varchar2(4000);
g2 boolean := false;
l NUMBER := 0;
m VARCHAR2(1000) :='';
c VARCHAR2(40) :='';
w VARCHAR2(40) :='';
sql_cond VARCHAR2(4000) :='';
ope VARCHAR2(5) :='';
OGG_FACT_TAB VARCHAR2(40);
OGG_COL_GROUP VARCHAR2(40);
OGG_COL_SUBGR VARCHAR2(40);
OGG_COL_SUM VARCHAR2(200);
OGG_COL_TIME VARCHAR2(40);
OGG_ALIAS_SUM VARCHAR2(100);
OGG_DIMS_TAB VARCHAR2(40);
OGG_COL_KEY VARCHAR2(40);
OGG_COL_DES VARCHAR2(40);
OGG_ALIAS_TAB VARCHAR2(100);
OGG_AVERAGES VARCHAR2(1);
OGG_CLASS NUMBER;
OGG_COL_SUM2 VARCHAR2(200);
cursor times is SELECT QTM_DESCRIPTION,QTM_DES_VALUE,QTM_VALUE FROM Q_TIME WHERE QTM_FIELD_NAME=t_time ORDER BY QTM_SEQUENCE;
BEGIN
select OGG_CLASS,OGG_FACT_TAB,OGG_COL_GROUP,OGG_COL_SUM,OGG_COL_TIME,OGG_ALIAS_SUM,OGG_DIMS_TAB,OGG_COL_KEY,OGG_COL_DES,OGG_ALIAS_TAB,OGG_COL_SUBGR,TRIM(OGG_AVERAGE),TRIM(OGG_COL_SUM2) into OGG_CLASS,OGG_FACT_TAB,OGG_COL_GROUP,OGG_COL_SUM,OGG_COL_TIME,OGG_ALIAS_SUM,OGG_DIMS_TAB,OGG_COL_KEY,OGG_COL_DES,OGG_ALIAS_TAB,OGG_COL_SUBGR,OGG_AVERAGES,OGG_COL_SUM2 from Q_OBJECT where OGG_CODE = cod;
IF ( p_subl is not null and p_subl <> 'null' and TRIM(p_subl) is not null and p_subl <> to_char(cod)
and OGG_COL_SUBGR IS not NULL and TRIM(OGG_COL_SUBGR) is not null ) THEN
g2:=true;
IF ( OGG_CLASS = 1 AND amount = 1 AND OGG_COL_SUM2 IS NOT NULL ) THEN
OGG_COL_SUBGR:='''kg''';
OGG_COL_SUM:=OGG_COL_SUM2;
ELSE
OGG_COL_SUBGR:='f.'||OGG_COL_SUBGR;
END IF;
END IF;
IF (OGG_AVERAGES is not null and OGG_AVERAGES='S') THEN
ope:='avg';
ELSE
ope:='sum';
END IF;
m:=t_time;
FOR reco IN times LOOP
c:=reco.QTM_DESCRIPTION;
m:=m||','||to_char(reco.QTM_VALUE)||','''||reco.QTM_DES_VALUE||'''';
END LOOP;
m:=m||',''...'')';
s:='select null link, mm "'||c||'", ii "'||OGG_ALIAS_SUM||' '||to_char(year)||'" from (';
s:=s||'select '||t_time||' tt, decode(t.'||m||' mm';
s:=s||', trunc('||ope||' ('||OGG_COL_SUM||'))'||' ii';
s:=s||' from D_TIME t, '||OGG_FACT_TAB||' f';
s:=s||' where t.TIM_AAAA = '||year;
s:=s||' and f.'||OGG_COL_TIME||' = t.TIM_KEY';
IF (g2) THEN
s:=s||' and '||OGG_COL_SUBGR||' = '''||p_subl||'''';
END IF;
IF (OGG_FACT_TAB='R_MAG_SALES' and w_warehouse is not null and w_warehouse <> 'null') THEN
s:=s||' and f.MS_WAREHOUSE = '''||w_warehouse||'''';
END IF;
IF (v_dims is not null and v_dims <> 'null') THEN
s:=s||' and f.'||OGG_COL_GROUP||' = '''||v_dims||'''';
END IF;
BEGIN
SELECT NVL(TRIM(SQL_CONDITION),'.') INTO sql_cond FROM Q_SQL_CONDITION WHERE SQL_CODE=cod_filter;
EXCEPTION
WHEN NO_DATA_FOUND THEN
sql_cond:='.';
END;
IF (sql_cond<>'.') THEN
s:=s||' and ('||sql_cond||')';
END IF;
s:=s||' group by t.'||t_time;
s:=s||' union ';
s:=s||'select t2.'||t_time||', decode(t2.'||m||', 0 from D_TIME t2';
s:=s||' where t2.'||t_time||' <> 0 and t2.'||t_time||' not in';
s:=s||' (select unique t3.'||t_time||' from D_TIME t3,'||OGG_FACT_TAB||' f3';
s:=s||' where t3.TIM_AAAA = '||year;
s:=s||' and t3.TIM_KEY = f3.'||OGG_COL_TIME;
BEGIN
SELECT NVL(TRIM(SQL_CONDITION),'.') INTO sql_cond FROM Q_SQL_CONDITION WHERE SQL_CODE=cod_filter;
EXCEPTION
WHEN NO_DATA_FOUND THEN
sql_cond:='.';
END;
IF (sql_cond<>'.') THEN
s:=s||' and ('||sql_cond||')';
END IF;
IF (OGG_FACT_TAB='R_MAG_SALES' and w_warehouse is not null and w_warehouse <> 'null') THEN
s:=s||' and f3.MS_WAREHOUSE = '''||w_warehouse||'''';
END IF;
IF (v_dims is not null and v_dims <> 'null') THEN
s:=s||' and f3.'||OGG_COL_GROUP||' = '''||v_dims||''')';
ELSE
s:=s||')';
END IF;
s:=s||' group by t2.'||t_time;
s:=s||')';
s:=s||' order by tt';
RETURN (s);
END;
To better understand this is the result of the function:
select null link, mm "Month", ii " 2018" from
(select TIM_MM tt, decode(t.TIM_MM,1,'January',2,'February',3,'March',4,'April',5,'May',6,'June',7,'July',8,'August',9,'September',10,'October',11,'November',12,'December','...') mm, trunc(sum (MS_FINAL_EXISTENCE)) ii from
D_TIME t, R_MAG_SALES f
where t.TIM_AAAA = 2018 and f.MS_TIM_BALANCE_DATE = t.TIM_KEY and f.MS_UNIT_OF_MEASURE = 'PZ' and f.MS_COD_CATEGORY = '000001' group by t.TIM_MM
union select t2.TIM_MM, decode(t2.TIM_MM,1,'January',2,'February',3,'March',4,'April',5,'May',6,'June',7,'July',8,'August',9,'September',10,'October',11,'November',12,'December','...'), 0 from
D_TIME t2
where t2.TIM_MM <> 0
and t2.TIM_MM not in (select unique t3.TIM_MM from D_TIME t3,R_MAG_SALES f3 where t3.TIM_AAAA = 2018 and t3.TIM_KEY = f3.MS_TIM_BALANCE_DATE and f3.MS_COD_CATEGORY = '000001') group by t2.TIM_MM) order by tt
Can enybody please help me, I just do not see what is wrong...
When you write the query in IR/CR, the bind variables :P2_OBJECT,:P2_YEAR are null.
So, your builder assumes the query as
select F_CHARTS(NULL,NULL,....)
into l_sql from dual;
So the compilation of PLSQL block fails
You can write the query as
select f_charts ( COALESCE(:P2_OBJECT, 'X'), COALESCE(:P2_YEAR, 0)....) from dual.
SO, you are actually sending some actual values to f_charts
You can also handle this in function to assume some value if inputs are null.
I have the next PL/SQL code:
create or replace FUNCTION NUMBER_PLATES (name VARCHAR2)
RETURN INT
IS
num_plates INT;
BEGIN
SELECT count(*) INTO num_plates
FROM plate p, detail_ped dt
WHERE dt.plate = p.cod_plate AND p.name = name;
RETURN (num_plates);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('ERROR');
END NUMBER_PLATES;
The next is execute the function into SQL Commands:
DECLARE a INT;
BEGIN
a := NUMBER_PLATES('chicken');
DBMS_OUTPUT.PUT_LINE(a);
END;
But the function returns me 0 when really is 3.
What am I doing wrong?
If I execute my SQL sentence returns 3:
SELECT count(*)
FROM plate p, detail_ped dt
WHERE dt.plate = p.cod_plate AND p.name = 'chicken';
You should rename parameter name to avoid name collision:
create or replace FUNCTION NUMBER_PLATES (p_name VARCHAR2)
RETURN INT
IS
num_plates INT;
BEGIN
SELECT count(*) INTO num_plates
FROM plate p
JOIN detail_ped dt -- proper JOIN syntax
ON dt.plate = p.cod_plate
WHERE p.name = p_name;
RETURN (num_plates);
-- exception is dead code, COUNT(*) will return 0 if no records found
-- EXCEPTION
-- WHEN NO_DATA_FOUND THEN
-- DBMS_OUTPUT.PUT_LINE('ERROR');
END NUMBER_PLATES;
/
db<>fiddle demo
I have this piece of code which I want to convert into a function , and call the function in a select statement by passing vendor_site_id from ap_supplier_sites_all. so that I can get the value of vendor_number i.e segment1 of ap_suppliers . here in this piece of code , I have a login to fetch first 6 digits from the column attribute52 of DFF table gecm_dff_ext .
DECLARE
v_ret_val VARCHAR2(30);
v_sup_gsl VARCHAR2(30);
v_vendor_id NUMBER;
BEGIN
v_vendor_id := '${PO.H_VENDOR_ID}';--> vendor_id column from ap_suppliers
IF <condition> = 'Y'
THEN
BEGIN
SELECT SUBSTR(ATTRIBUTE52,1,6)
INTO v_sup_gsl
FROM gecm_dff_ext
WHERE primary_table ='AP_SUPPLIER_SITES_ALL'
AND primary_key = '${PO.H_VENDOR_SITE_ID}';--> This value should be vendor_site_id from ap_supplier_sites_all table
EXCEPTION
WHEN OTHERS THEN
v_sup_gsl := NULL;
END;
BEGIN
IF v_sup_gsl IS NOT NULL THEN
SELECT segment1
INTO v_ret_val
FROM ap_suppliers
WHERE segment1 = v_sup_gsl;
END IF;
EXCEPTION
WHEN OTHERS THEN
v_ret_val := '';
END;
END IF;
IF v_sup_gsl IS NULL THEN
BEGIN
SELECT SEGMENT1
INTO v_ret_val
FROM ap_suppliers
WHERE vendor_id=v_vendor_id;
EXCEPTION
WHEN OTHERS THEN
v_ret_val := '';
END;
END IF;
:return_value:=v_ret_val;
END;
This is a simplified example: the following SELECT returns department name for a certain department number:
SQL> select dname
2 from dept
3 where deptno = &par_deptno;
Enter value for par_deptno: 10
DNAME
--------------
ACCOUNTING
SQL>
So, how to convert it to a function? By using a proper syntax, declaring a return variable, fetching into it and - returning the result:
SQL> create or replace function f_test (par_deptno in dept.deptno%type)
2 return dept.dname%type
3 is
4 retval dept.dname%type;
5 begin
6 select dname
7 into retval
8 from dept
9 where deptno = par_deptno;
10
11 return retval;
12 exception
13 when no_data_found then
14 return null;
15 end;
16 /
Function created.
SQL> select f_test(10) from dual;
F_TEST(10)
---------------------------------------------------------------------------
ACCOUNTING
SQL> select f_test(999) from dual;
F_TEST(999)
---------------------------------------------------------------------------
SQL>
This is what you should do. It seems that you are returning the v_ret_val, so - your code might look like this:
create or replace function f_test (par_vendor_id po.h_vendor_id%type)
return ap_suppliers.segment1%type
is
v_ret_val ap_suppliers.segment1%type;
begin
if <condition> = 'Y' then
...
end if;
return v_ret_val;
end;
What I have to do is to INSERT in table "info" different content, depending on the select result: if it is one row, no rows or more than one row.
I want to set the outretvalue variable on the exception section, then do the insert in the IF section, depending on outretvalue value.
Anyway, I get an error at compiling saying that f2 function is in an invalid state. I have 2 errors: for the INSERT and for not recognising rowcount. Why?
CREATE OR REPLACE FUNCTION f2 (v_nume employees.last_name%TYPE DEFAULT 'Bell')
RETURN NUMBER
IS
salariu employees.salary%type;
outretvalue number(2) := 0;
BEGIN
SELECT salary
INTO salariu
FROM employees
WHERE last_name = v_nume;
RETURN salariu;
EXCEPTION
WHEN NO_DATA_FOUND THEN
outretvalue := 1;
WHEN TOO_MANY_ROWS THEN
--at this row I have 2 errors: for the INSERT and for not recognising rowcount
INSERT INTO info(`no_lines`) VALUES(SQL%ROWCOUNT);
END f2;
/
SELECT f2('King') FROM dual;
Your function:
DECLARE
BEGIN
END;
... something
END;
Add another BEGIN at begin or move your IF inside existing BEGIN END block and remove second END.
EDIT: after clarification
CREATE OR REPLACE FUNCTION f2 (v_nume employees.last_name%TYPE DEFAULT 'Bell')
RETURN NUMBER
IS
salariu employees.salary%type;
outretvalue number(2) := 0;
BEGIN
SELECT salary
INTO salariu
FROM employees
WHERE last_name = v_nume;
RETURN salariu;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN -1;
WHEN TOO_MANY_ROWS THEN
SELECT count(*)
INTO salariu
FROM employees
WHERE last_name = v_nume;
INSERT INTO info(no_lines) VALUES(salariu);
RETURN -2;
WHEN OTHERS THEN
RETURN -3;
END f2;
/
SET SERVEROUTPUT on
DECLARE
l_ret NUMBER;
BEGIN
dbms_output.put_line(f2('Bell'));
dbms_output.put_line(f2('noBell'));
dbms_output.put_line(f2('King'));
END;
Try this. It will definelty help you out.
CREATE OR REPLACE FUNCTION f2(
v_nume employees.last_name%TYPE DEFAULT 'Bell')
RETURN NUMBER
IS
salariu employees.salary%type;
outretvalue NUMBER(2) := 0;
lv_cnt PLS_INTEGER;
BEGIN
SELECT salary INTO salariu FROM employees WHERE last_name = v_nume;
RETURN salariu;
EXCEPTION
WHEN NO_DATA_FOUND THEN
outretvalue := 1;
WHEN TOO_MANY_ROWS THEN
SELECT COUNT(1) INTO lv_cnt FROM employees WHERE last_name = v_nume;
INSERT INTO info
( no_lines
) VALUES
( lv_cnt
);
RETURN 2;
WHEN OTHERS THEN
RETURN 3;
END f2;
Oracle saves the compile errors in a table. I use the following query for retrieving the PL/SQL errors in my stored procs/funcs:
SELECT '*** ERROR in ' || TYPE || ' "' || NAME || '", line ' || LINE || ', position ' || POSITION || ': ' || TEXT
FROM SYS.USER_ERRORS
You could try running it and see if it helps identify the error in the function.
How to use BULK COLLECT and FORALL to replace CURSOR FOR LOOP in PL/SQL? I'd like to have a more efficient way to update records in a single table.
Suppose I have the following PL/SQL code:
DECLARE
var_buy_more_shoes inventory.buy_more_shoes%TYPE := NULL;
var_buy_more_bananas inventory.buy_more_bananas%TYPE := NULL;
var_buy_more_iphone6s inventory.buy_more_iphone6s%TYPE := NULL;
CURSOR cur
IS
SELECT *
FROM inventory
FOR UPDATE;
BEGIN
FOR rec IN cur
LOOP
IF rec.pair_of_shoes_left <= 100
THEN
var_buy_more_shoes := 'Yes';
END IF;
IF rec.weight_of_bananas_left <= 200
THEN
var_buy_more_bananas := 'Yes';
END IF;
IF rec.number_of_iphone6s_left <= 50
THEN
var_buy_more_iphone6s := 'Yes';
END IF;
UPDATE inventory a
SET A.buy_more_shoes = var_buy_more_shoes,
A.buy_more_bananas = var_buy_more_bananas,
A.buy_more_iphone6s = var_buy_more_iphone6s
WHERE CURRENT OF cur;
END LOOP;
COMMIT;
END;
Thanks.
This can be done in a single update statement:
UPDATE inventory
SET buy_more_shoes = CASE
WHEN pair_of_shoes_left <= 100 THEN 'Yes'
ELSE NULL
END
, buy_more_bananas = CASE
WHEN weight_of_bananas_left <= 200 THEN 'Yes'
ELSE NULL
END
, buy_more_iphone6s = CASE
WHEN number_of_iphone6s_left <= 50 THEN 'Yes'
ELSE NULL
END
You can create a table object and collections, then bulk collect the query results into the table collections.
CREATE OR REPLACE EDITIONABLE TYPE "F_OBJ" AS OBJECT (
Employer_name VARCHAR2(100),
Employer_id VARCHAR2 ( 100 ))
CREATE OR REPLACE EDITIONABLE TYPE "F_TAB" as table of _OBJ
create or replace function "fname"
return f_tab
is
l_f_tab f_tab;
begin
SELECT f_obj(employee_name, employee_id) bulk collect into f_tab from employee_table
return f_tab;