Oracle apex classic report - oracle

How can i create oracle classic report use this oracle function :
CREATE OR REPLACE function HR.pivot_func
return sys_refcursor
as
v_sql varchar2 (32767);
v_refcur sys_refcursor;
begin
v_sql :=
'select *
from (select current_position,
fk_department,
wages
from emp)
pivot (sum (wages)
for fk_department in (';
for r in
(select distinct fk_department
from emp)
loop
v_sql := v_sql || '''' || r.fk_department || ''',';
end loop;
v_sql := rtrim (v_sql, ',') || ')) order by 1';
open v_refcur for v_sql;
return v_refcur;
end pivot_func;
/

Create Classic Report based on function.

Related

How to generate dynamic SQL statement with generic condition

I am trying to generate a dynamic SQL statement with a generic condition.
Depending some conditions, it will be added different other conditions in where clause.
I was trying to have something like this:
declare
v_sql varchar2(500);
a number;
v_dummy number := 1;
begin
v_sql := 'delete tab_1 where v_dummy = 1 ';
if a = 1
then v_sql := v_sql || ' and col_1 = 1';
else v_sql := v_sql || ' and col_2 = 3';
end if;
execute immediate v_sql;
dbms_output.put_line(v_sql);
end;
The error raised is:
ORA-00904: "V_DUMMY": invalid identifier
Can anyone, please, guide me how to handle this situation?
The problem is with the definition of first condition (v_dummy = 1) that I need to add in order to use the "and" operand for the second condition.
Thank you,
If there is a possibility that you need multiple conditions (not in your code) you can set initial condition simply putting col_1 = col_1. No need for dummy variable at all and leaves you options to add some more conditions:
declare
v_sql varchar2(500);
a number;
begin
v_sql := 'delete tab_1 where col_1 = col_1 ';
if a = 1 then
v_sql := v_sql || ' and col_1 = 1';
else
v_sql := v_sql || ' and col_2 = 3';
end if;
execute immediate v_sql;
dbms_output.put_line(v_sql);
end;
Use a bind variable:
declare
v_sql varchar2(500);
a number;
v_dummy number := 1;
begin
v_sql := 'delete tab_1 where :v_dummy = 1 ';
if a = 1
then v_sql := v_sql || ' and col_1 = 1';
else v_sql := v_sql || ' and col_2 = 3';
end if;
execute immediate v_sql USING v_dummy;
dbms_output.put_line(v_sql);
end;
/
or since it evaluates to 1 = 1, you can omit it:
declare
v_sql varchar2(500);
a number;
begin
v_sql := 'delete tab_1';
if a = 1
then v_sql := v_sql || ' where col_1 = 1';
else v_sql := v_sql || ' where col_2 = 3';
end if;
execute immediate v_sql;
dbms_output.put_line(v_sql);
end;
/

Why do I get "ORA-00933: SQL command not properly ended" error ( execute immediate )?

I created a function and it uses a dynamic sql:
create function check_ref_value
(
table_name varchar2,
code_value number,
code_name varchar2
) return number is
l_query varchar2(32000 char);
l_res number;
begin
l_query := '
select sign(count(1))
into :l_res
from '|| table_name ||'
where '|| code_name ||' = :code_value
';
execute immediate l_query
using in code_value, out l_res;
return l_res;
end;
But when I try to use it I get an exception "ORA-00933: SQL command not properly ended"
What is wrong with this code?
You can use EXECUTE IMMEDIATE ... INTO ... USING ... to get the return value and DBMS_ASSERT to raise errors in the case of SQL injection attempts:
create function check_ref_value
(
table_name varchar2,
code_value number,
code_name varchar2
) return number is
l_query varchar2(32000 char);
l_res number;
begin
l_query := 'select sign(count(1))'
|| ' from ' || DBMS_ASSERT.SIMPLE_SQL_NAME(table_name)
|| ' where ' || DBMS_ASSERT.SIMPLE_SQL_NAME(code_name)
|| ' = :code_value';
execute immediate l_query INTO l_res USING code_value;
return l_res;
end;
/
Which, for the sample data:
CREATE TABLE abc (a, b, c) AS
SELECT 1, 42, 3.14159 FROM DUAL;
Then:
SELECT CHECK_REF_VALUE('abc', 42, 'b') AS chk FROM DUAL;
Outputs:
CHK
1
And:
SELECT CHECK_REF_VALUE('abc', 42, '1 = 1 OR b') AS chk FROM DUAL;
Raises the exception:
ORA-44003: invalid SQL name
ORA-06512: at "SYS.DBMS_ASSERT", line 160
ORA-06512: at "FIDDLE_UVOFONEFDEHGDQJELQJL.CHECK_REF_VALUE", line 10
As for your question:
What is wrong with this code?
Using SELECT ... INTO is only valid in an SQL statement in a PL/SQL block and when you run the statement via EXECUTE IMMEDIATE it is executed in the SQL scope and not a PL/SQL scope.
You can fix it by wrapping your dynamic code in a BEGIN .. END PL/SQL anonymous block (and reversing the order of the bind parameters in the USING clause):
create function check_ref_value
(
table_name varchar2,
code_value number,
code_name varchar2
) return number is
l_query varchar2(32000 char);
l_res number;
begin
l_query := '
BEGIN
select sign(count(1))
into :l_res
from '|| DBMS_ASSERT.SIMPLE_SQL_NAME(table_name) ||'
where '|| DBMS_ASSERT.SIMPLE_SQL_NAME(code_name) ||' = :code_value;
END;
';
execute immediate l_query
using out l_res, in code_value;
return l_res;
end;
/
(However, that is a bit more of a complicated solution that just using EXECUTE IMMEDIATE ... INTO ... USING ....)
db<>fiddle here

Can we use collection variable as a table in execute immediate?

Declare
Type t_approved_node is record( node_rowid Hr
node_rowid%type, Node_+type hr.node_type%type);
Type t_val is table of t_approved_node Index by pls_integer;
V_node t_val;
V_tab varchar2(20);
V_col varchar2(400);
V_nrf_flg hr.hr_flag%type;
V_ubrf_flg hr.hr_flag%type := 3;
V_col_str varchar2(4000);
Begin
Begin
Select hr_flag into v_nrf_flg from hr;
End;
Begin
Select h.node_rowid, h.node_type bulk collect into v_node
from hr h, hr_attr_wfm haw
Where h.hr_relation_id = haw.uc_hr_relation_id
And h.node_type = 'UBR';
Begin
V_tab := 'UC_UBR';
Select listagg(column_name, ',' within group(order by
column_id)
Into v_col from user_tab_columns where table_name = v_tab;
End;
V_col_str := regex_replace( v_col, 'HR_FLAG', v_ubrf_flg);
Execute immediate ' insert into ' || v_tab || '( ' ||
V_col || ') ' || ' select '|| v_col_str || ' from ' ||
V_tab || 'R ' || q' [ where node_type = ' UBR' a
and hr_flag =:1 and exists( ] ' || ' select 1 ' || ' from table( ' ||
v_node || ')y' || q' [ where y.node_rowid = R.node_rowid ] )'
Using v_nrf_flag;
End;
End;
I was trying to execute above block getting below error.
Wrong number or types of arguments in call to ||
Final query should be like
insert into UC_UBR ( v_col)/*3 columns into v_col variable*/
select v_col_str /* 3 columns in v_col_str variable*/ from UC_UBR R where hr_flag =:1
and exists
(select 1 from table(v_node) /*collection variable*/ y
where y.node_rowid = r.node_rowod;
Can anyone help on this?
Your sample code is full of errors and does not make any sense at all. But if I focus on your question, then the answer is "yes". See this example:
CREATE OR REPLACE TYPE t_app AS OBJECT( nodeid NUMBER, Nodetype VARCHAR2(100));
CREATE OR REPLACE TYPE t_val IS TABLE OF t_app;
DECLARE
V_node t_val;
V_result t_val;
V_app t_app;
V_count NUMBER;
Sql_stmt VARCHAR2(100);
nodeid NUMBER;
Nodetype VARCHAR2(100);
BEGIN
SELECT t_app(nodeid, Nodetype) BULK COLLECT INTO V_node FROM HR;
Sql_stmt := 'SELECT count(*) FROM TABLE(:t)';
EXECUTE IMMEDIATE Sql_stmt INTO V_count USING V_node;
DBMS_OUTPUT.PUT_LINE ( 'V_count = ' || V_count );
Sql_stmt := 'SELECT nodeid, Nodetype FROM TABLE(:t) WHERE ROWNUM = 1';
EXECUTE IMMEDIATE Sql_stmt INTO nodeid, Nodetype USING V_node;
DBMS_OUTPUT.PUT_LINE ( 'nodeid = ' || nodeid );
DBMS_OUTPUT.PUT_LINE ( 'Nodetype = ' || Nodetype );
Sql_stmt := 'SELECT t_app(nodeid, Nodetype) FROM TABLE(:t) WHERE ROWNUM = 1';
EXECUTE IMMEDIATE Sql_stmt INTO V_app USING V_node;
DBMS_OUTPUT.PUT_LINE ( 'V_app = ' || XMLTYPE(V_app).getClobVal() );
Sql_stmt := 'SELECT t_app(nodeid, Nodetype) FROM TABLE(:t)';
EXECUTE IMMEDIATE Sql_stmt BULK COLLECT INTO V_result USING V_node;
END;
It would help if you posted real code you used, because this is full of syntax errors (missing sql_stmt local variable declaration, put.line (?)).
I have no idea what you plan to do with such a select statement as you can't execute it, it doesn't make any sense but - here you go; see line #20.
SQL> set serveroutput on
SQL>
SQL> DECLARE
2 TYPE t_app IS RECORD
3 (
4 nodeid NUMBER,
5 Nodetype VARCHAR2 (20)
6 );
7
8 TYPE t_val IS TABLE OF t_app
9 INDEX BY PLS_INTEGER;
10
11 V_node t_val;
12 V_tab VARCHAR2 (20);
13
14 sql_stmt VARCHAR2 (200);
15 BEGIN
16 SELECT empno, ename
17 BULK COLLECT INTO v_node
18 FROM emp;
19
20 Sql_stmt := 'select 1 from (' || v_node (1).nodeid || 'Y)';
21
22 DBMS_OUTPUT.put_line (sql_stmt);
23 END;
24 /
select 1 from (7369Y)
PL/SQL procedure successfully completed.
SQL>

Diffing 2 rows of 2 tables based on column values (without prior knowledge of columns names)

I don't even know if it is possible - so any idea will be welcome:
I want a function, which will return a string(varchar2) representation of the differences in values of columns of 2 specific tables.
So it's task will be to
find difference between 2 rows that belong to 2 tables (which happens
to have the same structure).
Consider the following scenario.
Table A (A_rowid,col1,col2,col3,col4,col5...,coln) and values (id1,val1,val2,val3,..,valn)
Table B (B_rowid,col1,col2,col3,col4,col5...,coln) and values (id2,val1,val2',val3,..,valn')
*A_rowid - unique key of tableA, B_rowid - unique key of table B
fnction diff(A_rowid number, B_rowid number) returns varchar2 is
begin
--do something
end;
All columns of the tables could be treated as Varchar2.
Thus,
The expected output would be -->
Null if no difference is found
or
diff: col2:val2->val2', coln:valn->valn'
What is important here is that I would like to do that without hard coding column names
(table names are hard coded though).
e.g. if and when we add additional columns to our tables - function should still work.
You can use this one:
FUNCTION diff(A_rowid NUMBER, B_rowid NUMBER) RETURN VARCHAR2 IS
CURSOR TabColumns IS
SELECT COLUMN_NAME, COLUMN_ID
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = 'TABLE_A'
AND COLUMN_NAME <> 'A_ROWID'
ORDER BY COLUMN_ID;
sqlstr VARCHAR2(1000);
val_a VARCHAR2(4000);
val_b VARCHAR2(4000);
res VARCHAR2(30000);
BEGIN
FOR aCol IN TabColumns LOOP
BEGIN
sqlstr := 'SELECT a.'||aCol.COLUMN_NAME||', b.'||aCol.COLUMN_NAME;
sqlstr := sqlstr ||' FROM TABLE_A a CROSS JOIN TABLE_B b ';
sqlstr := sqlstr || ' WHERE A_rowid = :aRow AND B_rowid = :bRow ';
sqlstr := sqlstr || ' AND LNNVL(a.'||aCol.COLUMN_NAME||' = b.'||aCol.COLUMN_NAME||') ';
sqlstr := sqlstr || ' AND COALESCE(a.'||aCol.COLUMN_NAME||', b.'||aCol.COLUMN_NAME||') IS NOT NULL ';
EXECUTE IMMEDIATE sqlstr INTO val_a, val_b USING A_rowid, B_rowid;
res := res ||', '||aCol.COLUMN_NAME||':'||val_a||'->'||val_b;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END;
END LOOP;
RETURN REGEXP_REPLACE(res, '^, ', 'diff:');
END;
Note, function LNNVL(a.'||aCol.COLUMN_NAME||' = b.'||aCol.COLUMN_NAME||') is needed in case of NULL values.
Condition a.COLUMN_NAME <> b.COLUMN_NAME does return anything when one of the values is NULL.
LNNVL(a.COLUMN_NAME = b.COLUMN_NAME) is equivalent to
( a.COLUMN_NAME <> b.COLUMN_NAME
OR (a.COLUMN_NAME IS NULL AND b.COLUMN_NAME IS NOT NULL)
OR (a.COLUMN_NAME IS NOT NULL AND b.COLUMN_NAME IS NULL) )
However, use function above only if you are not concerned about performance. The more advanced solution would be this one:
FUNCTION diff(A_rowid NUMBER, B_rowid NUMBER) RETURN VARCHAR2 IS
CURSOR TabColumns IS
SELECT COLUMN_NAME, COLUMN_ID
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = 'TABLE_A'
AND COLUMN_NAME <> 'A_ROWID'
ORDER BY COLUMN_ID;
sqlstr VARCHAR2(10000);
val_a VARCHAR2(4000);
val_b VARCHAR2(4000);
res VARCHAR2(30000);
cur INTEGER;
p INTEGER;
res INTEGER;
BEGIN
sqlstr := 'SELECT '
FOR aCol IN TabColumns LOOP
sqlstr := ' a.'||aCol.COLUMN_NAME||'_A, b.'||aCol.COLUMN_NAME||'_B, ';
END LOOP;
sqlstr := REGEXP_REPLACE(sqlstr, ', $', ' FROM TABLE_A a CROSS JOIN TABLE_B b ');
sqlstr := sqlstr || ' WHERE A_rowid = :aRow AND B_rowid = :bRow ';
cur := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(cur, sqlStr, DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE (cur, ':aRow', A_rowid);
DBMS_SQL.BIND_VARIABLE (cur, ':bRow', B_rowid);
p := 1;
FOR aCol IN TabColumns LOOP
DBMS_SQL.DEFINE_COLUMN(cur, p, aCol.COLUMN_NAME||'_A', 4000);
DBMS_SQL.DEFINE_COLUMN(cur, p+1, aCol.COLUMN_NAME||'_B', 4000);
p := p + 2;
END LOOP;
res := DBMS_SQL.EXECUTE_AND_FETCH(cur, TRUE);
p := 1;
FOR aCol IN TabColumns LOOP
DBMS_SQL.COLUMN_VALUE(cur, p, val_a);
DBMS_SQL.COLUMN_VALUE(cur, p+1, val_b);
p := p + 2;
IF val_a <> val_b OR (val_a IS NULL AND val_b IS NOT NULL) OR (val_a IS NOT NULL AND val_b IS NULL) THEN
res := res ||', '||aCol.COLUMN_NAME||':'||val_a||'->'||val_b;
END IF;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(cur);
RETURN REGEXP_REPLACE(res, '^, ', 'diff:');
END;
Try this function
FUNCTION getdiff(arowid varchar, browid varchar) RETURN CLOB IS
v_line clob;
v_col_cnt INTEGER;
v_ind NUMBER;
rec_tab dbms_sql.desc_tab;
v_cursor NUMBER;
v_sql clob;
V_FIRST clob;V_SECOND CLOB;
V_FINAL CLOB;
begin
V_SQL := Q'$ select * from(select * from Table1 where rowid=:arowid)a,
(select * from Table2 where rowid=:browid)b $';
V_CURSOR := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(V_CURSOR, V_SQL, DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE (V_CURSOR, ':arowid', arowid);
DBMS_SQL.BIND_VARIABLE (V_CURSOR, ':browid', browid);
DBMS_SQL.DESCRIBE_COLUMNS(V_CURSOR, V_COL_CNT, REC_TAB);
FOR V_POS IN 1 .. REC_TAB.LAST LOOP
V_LINE := REC_TAB(V_POS).COL_NAME;
DBMS_SQL.DEFINE_COLUMN(V_CURSOR, V_POS, V_LINE);
END LOOP;
V_IND := DBMS_SQL.EXECUTE(V_CURSOR);
LOOP
V_IND := DBMS_SQL.FETCH_ROWS(V_CURSOR);
EXIT WHEN V_IND = 0;
FOR V_COL_SEQ IN 1 .. REC_TAB.COUNT LOOP
if v_col_seq <=V_COL_CNT/2 then
DBMS_SQL.COLUMN_VALUE(V_CURSOR, V_COL_SEQ, V_LINE);
V_FIRST := V_LINE;
DBMS_SQL.COLUMN_VALUE(V_CURSOR, V_COL_SEQ+3, V_LINE);
V_SECOND := V_LINE;
IF V_FIRST <> V_SECOND THEN
V_FINAL := V_FINAL || rec_tab(v_col_seq).col_name || ':' || V_FIRST ||'->'||V_SECOND || ',';
END IF;
end if;
END LOOP;
END LOOP;
RETURN V_FINAL;
end;
output of getdiff function is in clob format because limit of varchar2 datatype is 32767 so after reach limit function give us error.
Use:
select to_char(getdiff('AAAjOuAAEAAA697AAC','AAAjOuAAEAAA697AAk')) from dual;
here to_char function is used for convert clob data format to char,so thats give us perfect string output.

Comma separated values to IN function in oracle

I am trying to execute below query, but not getting any result.
Could some one tell what wrong I am doing?.
DECLARE
object_types VARCHAR2(200);
v_object_types VARCHAR2(200);
l_count number;
BEGIN
object_types :='TABLE,VIEW';
select ''''||regexp_replace(object_types, '( )*(,)( )*',''',''')||''''
into v_object_types from dual;
dbms_output.put_line(to_char(v_object_types));
SELECT count(*) into l_count
FROM all_objects o where o.object_type IN ('||v_object_types||');
dbms_output.put_line(l_count);
END;
WHERE variable IN (1,2,3)
is different to what you are sending now
WHERE variable IN ('1,2,3') or
WHERE variable IN ('||v_object_types||')
You are trying to build the SQL dynamically but instead you are using a single string literal '||v_object_types||' in the IN clause.
You can do it using a collection:
Oracle Setup:
CREATE TYPE stringlist IS TABLE OF VARCHAR2(200);
/
PL/SQL:
DECLARE
object_types VARCHAR2(200) := 'TABLE,VIEW';
v_object_types stringlist;
BEGIN
SELECT TRIM( BOTH FROM REGEXP_SUBSTR( object_types, '[^,]+', 1, LEVEL ) )
BULK COLLECT INTO v_object_types
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( object_types, '[^,]+' );
FOR i IN 1 .. v_object_types.COUNT LOOP
dbms_output.put_line( v_object_types(i) );
END LOOP;
SELECT count(*)
INTO l_count
FROM all_objects
WHERE object_type MEMBER OF v_object_types;
dbms_output.put_line(l_count);
END;
/
or using dynamic sql:
DECLARE
object_types VARCHAR2(200) := 'TABLE,VIEW';
v_sql VARCHAR2(200);
l_count INTEGER;
BEGIN
v_sql := 'SELECT count(*) FROM all_objects WHERE object_type IN ('
|| REGEXP_REPLACE( object_types, ' *(.+?) *(,|$)', '''\1''\2' )
|| ')';
EXECUTE IMMEDIATE v_sql INTO l_count;
dbms_output.put_line(l_count);
END;
/

Resources