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 a PL/SQL block that I put into a stored procedure so that I can debug it. Here is a bit of pseudocode to describe the PL/SQL block:
CREATE OR REPLACE PROCEDURE SPROC_A AS
CURSOR CURSOR_A IS
SELECT ID, YEAR
FROM TABLE_A;
vNAME VARCHAR2(200) := NULL;
BEGIN
FOR REC IN CURSOR_A LOOP
BEGIN
SELECT NVL(B.FIELD_1, B.FIELD_2) INTO vNAME --the sproc breaks here--
FROM TABLE_B B
WHERE B.ID = REC.ID
AND B.YEAR = REC.YEAR
EXCEPTION
WHEN NO_DATA_FOUND THEN
vNAME := NULL;
END;
UPDATE TABLE_C C
SET C.NAME = vNAME
WHERE C.ID = REC.ID
AND C.YEAR = REC.YEAR;
END LOOP;
END;
If I run this portion independently and supply my own values for REC.ID and REC.YEAR, data is returned:
SELECT NVL(B.FIELD_1, B.FIELD_2) --INTO vNAME
FROM TABLE_B B
WHERE B.ID = REC.ID
AND B.YEAR = REC.YEAR
Furthermore, the variable into which I am trying to select data is of datatype VARCHAR2(200) - TABLE_B.FIELD_1 and TABLE_B.FIELD_2 are both of datatype VARCHAR2(40), so there shouldn't be a mismatch of datatypes.
What else could I be missing here?
i have a table that the content of it:
ID A B
--- --- ----
1 123 &%df
2 587 364
A function was written to find invalid number(below script)
CREATE OR REPLACE FUNCTION is_number RETURN number IS
A_var number(15);
B_var number(15);
BEGIN
for u in (select id,
A,
B,
from TABLE_NAME) LOOP
BEGIN
select
to_number(u.A),
to_number(u.B),
into A_var , B_var
from dual;
EXCEPTION
WHEN others THEN
update TABLE_NAME set ?????? = null where id = u.id;
END;
END LOOP;
return -1;
END is_number;
the top script I want to replace ?????? to name of column as invalid number that in this example name of column is B
after excution top script,content of table is:
ID A B
--- --- ----
1 123
2 587 364
What if number conversion errors will be in both columns?
Try this:
create or replace function is_number return number is
a_var number(15);
b_var number(15);
function custom_to_number(v varchar2) return number is
result number;
begin
result:=to_number(v);
return result;
exception when others then
if(sqlcode = -6502) then
return null;
end if;
raise;
end;
begin
for u in (select id
,a
,b
from table_name)
loop
a_var:=custom_to_number(u.a);
b_var:=custom_to_number(u.b);
if((u.a is not null and a_var is null) or (u.b is not null and b_var is null)) then
update table_name
set a = case when a_var is null then null else a end
,b = case when b_var is null then null else b end
where id = u.id;
end if;
end loop;
return -1;
end is_number;
If your Intend is to replace the columns which contains alphanuneric data to NULL in the table, then you could use a Trigger to acheive this.
CREATE OR REPLACE TRIGGER trigger_Name BEFORE INSERT OR UPDATE OF
A_var, B_var ON TABLE_NAME
FOR EACH ROW
BEGIN
IF NOT((regexp_like (:NEW.A_var,'^[[:digit:]]+$'))) THEN
:NEW.A_var := NULL;
END IF;
IF NOT((regexp_like (:NEW.B_var,'^[[:digit:]]+$'))) THEN
:NEW.B_var := NULL;
END IF;
INSERT INTO TABLE_NAME(A_var, B_var) Values(:NEW.A_var, :NEW.B_var);
END;
I have one table that holds a record for each customer (main table). I then have a table with additional detail for some customers. The additional detail table sometimes has no records for a record in the main table. Sometimes the detail table has multiple records for a record in the main table & if this is the case I need the most recent record (hence the max subselect).
The trouble is my function only returns values for the few records in the detail table. If I comment out the portion of the function that looks at the detail table and just return the STAT3 value it seems to work. How do I make the second select statment below only apply if there is a result for that query?
create or replace FUNCTION "F_RETURN_STAT" (
N_UNIQUE IN NUMBER)
RETURN VARCHAR2
IS
V_STAT3 varchar2(20);
V_STAT varchar2(20);
V_STAT2 varchar2(20);
D_ACTDATE date;
D_STARTDATE date;
BEGIN
select expire into D_ACTDATE
from main_table a
where a.uniquefield = N_UNIQUE;
IF
D_ACTDATE > SYSDATE
or
D_ACTDATE is null
then
V_STAT :='TRUE';
else
v_STAT :='FALSE';
end if;
select b.startdate into D_STARTDATE
from main_table a, detail_table b
where a.uniquefield= b.main_table_id(+) and
b.main_table_id = N_UNIQUE and
b.uniquefield in
(select max(c.uniquefield) from detail_table c group by main_table_id);
if
D_STARTDATE is not null
then
V_STAT2 :='FALSE';
end if;
if
V_STAT2 ='FALSE'
then
V_STAT3 :='FALSE';
ELSE
V_STAT3 := V_STAT;
end if ;
RETURN(V_STAT3);
end;
I think this version of your function will solve your problem:
CREATE OR REPLACE FUNCTION f_return_stat(n_unique IN NUMBER)
RETURN VARCHAR2 IS
v_stat3 VARCHAR2(20);
v_stat VARCHAR2(20);
v_stat2 VARCHAR2(20);
d_actdate DATE;
d_startdate DATE;
BEGIN
--First Query
SELECT expire
INTO d_actdate
FROM main_table a
WHERE a.uniquefield = n_unique;
IF d_actdate > SYSDATE OR d_actdate IS NULL THEN
v_stat := 'TRUE';
ELSE
v_stat := 'FALSE';
END IF;
BEGIN
--Second Query
SELECT b.startdate
INTO d_startdate
FROM detail_table b
WHERE b.main_table_id = n_unique
AND b.uniquefield IN (SELECT MAX(c.uniquefield)
FROM detail_table c
GROUP BY main_table_id);
EXCEPTION
WHEN NO_DATA_FOUND THEN
d_startdate := NULL;
END;
IF d_startdate IS NOT NULL THEN
v_stat2 := 'FALSE';
END IF;
IF v_stat2 = 'FALSE' THEN
v_stat3 := 'FALSE';
ELSE
v_stat3 := v_stat;
END IF;
RETURN (v_stat3);
END;
In your version of the second query, your join (a.uniquefield= b.main_table_id) and your filter (b.main_table_id = N_UNIQUE) are equivalent, so main_table a can be removed altogether. The only reason to leave it in is to make sure that your query always returns a row. If you use exception handling to catch the NO_DATA_FOUND exception, that need goes away and you can simplify your query to just select from detail_table b.
I believe there could be a more efficient way however this might do the job:
SELECT b.startdate
INTO d_startdate
FROM detail_table b
WHERE b.main_table_id = n_unique
and
(b.uniquefield in
(select max(c.uniquefield) from detail_table c group by main_table_id)
or b.uniquefield is null);