passing in table name as plsql parameter - oracle

I want to write a function to return the row count of a table whose name is passed in as a variable. Here's my code:
create or replace function get_table_count (table_name IN varchar2)
return number
is
tbl_nm varchar(100) := table_name;
table_count number;
begin
select count(*)
into table_count
from tbl_nm;
dbms_output.put_line(table_count);
return table_count;
end;
I get this error:
FUNCTION GET_TABLE_COUNT compiled
Errors: check compiler log
Error(7,5): PL/SQL: SQL Statement ignored
Error(9,8): PL/SQL: ORA-00942: table or view does not exist
I understand that tbl_nm is being interpreted as a value and not a reference and I'm not sure how to escape that.

You can use dynamic SQL:
create or replace function get_table_count (table_name IN varchar2)
return number
is
table_count number;
begin
execute immediate 'select count(*) from ' || table_name into table_count;
dbms_output.put_line(table_count);
return table_count;
end;
There is also an indirect way to get number of rows (using system views):
create or replace function get_table_count (table_name IN varchar2)
return number
is
table_count number;
begin
select num_rows
into table_count
from user_tables
where table_name = table_name;
return table_count;
end;
The second way works only if you had gathered statistics on table before invoking this function.

Related

Executing function embedded SELECT sql query to increase performance

I have the following SQL query that will return the name of a column in a specific table. Let's say it return 'USER_PK' as column name when it runs.
the query:
SELECT max(COLUMN_NAME)
FROM ALL_TAB_COLUMNS
WHERE OWNER= 'DW_01'
AND table_name='D_O_USERS'
AND COLUMN_NAME<>'USER_PK';
Now I would like to run the above query as part of a function but instead of running it and storing the value it returns in a variable (using INTO or attribution like initial_sql: = '...', followed by exec ) I would need to have it run inside one line of code as below (see part in bold)... So far I have been unsuccessful as it is interpreted as a string when using quotes ...
CREATE OR REPLACE function DW_01.EXECUTE_AUTO (db_schema IN VARCHAR2, db_table IN VARCHAR2, pk_name IN VARCHAR2, id_pk IN INTEGER) RETURN VARCHAR2
IS
result VARCHAR2(4000);
begin
EXECUTE IMMEDIATE 'select STANDARD_HASH( '|| **SELECT max( COLUMN_NAME) FROM ALL_TAB_COLUMNS WHERE OWNER='' || db_schema || '' AND table_name=''||db_table ||'' AND COLUMN_NAME<>'' ||pk_name ||'** ,''SHA512'' ) from '||db_table||' where '|| pk_name ||'='||id_pk into RESULT ;
return result;
end;
Many thanks in advance for your thoughts!
You need to amend you r code like below -
CREATE OR REPLACE function DW_01.EXECUTE_AUTO (db_schema IN VARCHAR2,
db_table IN VARCHAR2,
pk_name IN VARCHAR2,
id_pk IN INTEGER) RETURN VARCHAR2
IS
result VARCHAR2(4000);
begin
EXECUTE IMMEDIATE 'select STANDARD_HASH( ' || pk_name || ',256 )
from '||db_table||' where '|| pk_name ||'='||id_pk into RESULT;
return result;
end;
/
There are only a few ways to run dynamic SQL in SQL, and they're neither pretty nor fast. The function below uses DBMS_XMLGEN.GETXML to dynamically run a SQL statement.
create or replace function execute_auto(db_schema in varchar2, db_table in varchar2, pk_name in varchar2, id_pk in integer) return varchar2
is
v_column_name varchar2(128);
v_result varchar2(4000);
begin
select standard_hash(to_number(extractValue(xml_results, '/ROWSET/ROW/' || max_column)), 'SHA512') hash_value
into v_result
from
(
--Create a single XML file with the ROWIDs that match the condition.
select max(column_name) max_column, xmltype(dbms_xmlgen.getxml
('
select '||max(column_name)||'
from '||db_schema||'.'||db_table||'
where id = '||id_pk
)) xml_results
from all_tab_columns
where owner = db_schema
and table_name = db_table
and column_name <> pk_name
);
return v_result;
end;
/
For example, let's create this sample table with 100,000 rows:
--drop table test1;
create table test1(id number, a number, b number, constraint pk_test1 primary key(id));
insert into test1
select level, level, level from dual connect by level <= 100000;
commit;
This shows how to use the function;
select execute_auto(user, 'TEST1', 'ID', id) hash
from test1 where id = 1;
HASH
----
A36753F534728ED84A463ECB13750B8E920A7E4D90244258DE77D9800A0F3DAF8CBAD49602E960A2355933C689A23C30377CE10FC4B8E1F197739FF86C791022
In addition to problems with type conversion and SQL injection, the performance is terrible. Selecting all 100,000 rows this way takes 200 seconds on my machine.
select sum(length(execute_auto(user, 'TEST1', 'ID', id))) from test1;
Normally, running everything in a single SELECT statement is a good way to improve performance. But this extreme type of dynamic SQL will never run fast. You probably want to rethink your approach. Instead of trying to optimize the SQL inside a function that is run one-row-at-a-time, try to change the process to process once-per-table

use pipelined function to get result set from multi-tables join

i want to create a function to get selectivity of a table, the input parameter is owner and table name, i searched and find pipelined function can be used here, i used the following codes but i get error for creating function,
create or replace type ROW_TYPE as object
(
column_name varchar2(128),
num_rows number,
num_distinct number
);
/
create or replace type TABLE_TYPE as table of ROW_TYPE;
/
create or replace function getSelectivity(owner in varchar2, tab_name in varchar2)
return TABLE_TYPE
PIPELINED
IS
BEGIN
for myrow in(
select a.column_name,b.num_rows,a.num_distinct
from dba_tab_col_statistics a, dba_tables b
where a.owner = b.owner and a.table_name = b.table_name and a.owner=upper(owner) and
a.table_name =upper(tab_name)
)loop
pipe row(ROW_TYPE(myrow.column_name,myrow.num_rows,myrow.num_distinct));
end loop;
return;
end;
/
error:
SQL> show errors;
Errors for FUNCTION MOVIL.GETSELECTIVITY:
LINE/COL ERROR
-------- -------------------------------
9/88 PL/SQL: ORA-00918: undefined column
7/14 PL/SQL: SQL Statement ignored
12/20 PLS-00364: Invalid usage of Loop index variable 'MYROW'
12/2 PL/SQL: Statement ignored
but if i just query one single table, then everything is fine,
create or replace type ROW_TYPE as object
(
column_name varchar2(128),
num_distinct number
);
/
create or replace type TABLE_TYPE as table of ROW_TYPE;
/
create or replace function getSelectivity(owner in varchar2, tab_name in varchar2)
return TABLE_TYPE
PIPELINED
IS
BEGIN
for myrow in(
select a.column_name,a.num_distinct
from dba_tab_col_statistics a
where a.owner=upper(owner) and a.table_name =upper(tab_name)
)loop
pipe row(ROW_TYPE(myrow.column_name,myrow.num_distinct));
end loop;
return;
end;
/
does pipelined function do not support multi-tables joining ?
You can create a view on the tables like the below and use it in your function
create or replace view test_db_view as select
a.column_name,b.num_rows,a.num_distinct,
a.table_name,a.owner
from all_tab_col_statistics a, all_tables b
where a.owner = b.owner and a.table_name = b.table_name
create or replace function getSelectivity(owner in varchar2, tab_name in varchar2)
return TABLE_TYPE
PIPELINED
IS
BEGIN
for myrow in( select * from test_db_view a where a.owner=upper(owner) and
a.table_name =upper(tab_name)
)loop
pipe row(ROW_TYPE(myrow.column_name,myrow.num_rows,myrow.num_distinct));
end loop;
return;
end;
OR you can try using p_owner instead of owner in function getSelectivity parameter

Create function returns sys_refcursor

How to create a function to display all employees of one department?
I'd tried this code but it returns only "cursor" value.
CREATE OR REPLACE FUNCTION emp_dept (dept_id IN NUMBER)
RETURN SYS_REFCURSOR
IS
emp_name SYS_REFCURSOR;
BEGIN
OPEN emp_name
FOR SELECT last_name
FROM employees
WHERE department_id = dept_id;
RETURN emp_name;
END emp_dept;
You may use these options to read and display output from the cursor that's returned from your function.
Use a simple select
select emp_dept(10) from dual;
Result
EMP_DEPT(10)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
LAST_NAME
-------------------------
Whalen
Use DBMS_SQL.RETURN_RESULT ( Oracle 12c and above)
DECLARE
l_cur SYS_REFCURSOR := emp_dept(10) ;
BEGIN
DBMS_SQL.RETURN_RESULt(l_cur);
END;
/
Result
PL/SQL procedure successfully completed.
ResultSet #1
LAST_NAME
-------------------------
Whalen
FETCH from the cursor into a local collection. A slight variation could be also be used to fetch into a scalar variable.
DECLARE
l_cur SYS_REFCURSOR := emp_dept(10) ;
TYPE l_last_name_tab IS TABLE OF employees.last_name%TYPE;
l_lnt l_last_name_tab;
BEGIN
FETCH l_cur BULK COLLECT INTO l_lnt;
for i in 1..l_lnt.count
loop
dbms_output.put_line(l_lnt(i));
end loop;
END;
/
Result
Whalen
PL/SQL procedure successfully completed.

IN parameter to Oracle Function

I have an Oracle function which accepts few IN parameters of number and varchar2 datatype and also it return a number datatype. Within the function, I'm selecting from a table with the IN parameters are applied in the 'WHERE' clause. By doing this, this filters is not applied to the select query instead it is skipping that condition. I need to explicitly declare a variables to store the values from IN parameter and then I need to use these variables in the WHERE clause. Not sure why this is required. Could you please help to resolve this without using the additional variables.
Below is the function, which I am using.
create or replace FUNCTION AGG_NC8B_FN (
FACTORY_ID NUMBER,
REPORTING_PERIOD NUMBER,
GRANULARITY_TYPE_TXT VARCHAR2,
GRANULAROTY_LEVEL_TXT VARCHAR2,
CONDITION_ID NUMBER)
RETURN NUMBER
IS
CAL_VALUE OCT_DMA_MOMIS.FCT_KSTACK_AGGREGATION.AGGREGATE_VALUE_DEC%TYPE;
-- temporary variables --not sure why this is required?
A number;
B number;
C varchar2(100);
D varchar2(100);
E number;
BEGIN
A:=FACTORY_ID;
B:=REPORTING_PERIOD;
C:=GRANULARITY_TYPE_TXT;
D:=GRANULAROTY_LEVEL_TXT;
E:=CONDITION_ID;
SELECT DECODE(B.AGGREGATE_VALUE_DEC,0,0,((A.AGGREGATE_VALUE_DEC/B.AGGREGATE_VALUE_DEC)*100))
INTO CAL_VALUE
FROM (SELECT AGGREGATE_VALUE_DEC
FROM OCT_DMA_MOMIS.FCT_KSTACK_AGGREGATION
WHERE FK_FIGURE_ID =30121
AND FK_FACTORY_ID =A
AND LK_REPORTING_PERIOD =B
AND GRANULARITY_TYPE_TXT=C
AND GRANULAROTY_LEVEL_TXT=D
AND FK_CONDITION_ID =E) A ,
(SELECT AGGREGATE_VALUE_DEC
FROM OCT_DMA_MOMIS.FCT_KSTACK_AGGREGATION
WHERE FK_FIGURE_ID =30122
AND FK_FACTORY_ID =A
AND LK_REPORTING_PERIOD =B
AND GRANULARITY_TYPE_TXT=C
AND GRANULAROTY_LEVEL_TXT=D
AND FK_CONDITION_ID =E
) B ;
RETURN CAL_VALUE;
END AGG_NC8B_FN;
Your function parameters and columns have same names (GRANULARITY_TYPE_TXT for example). Use different names or qualify function parameters with the name of the function:
-- wrong:
declare
function f(object_name in varchar2) return number is
l_id number;
begin
select object_id
into l_id
from all_objects
where object_name = object_name -- equals to (object name is not null)
and object_type = 'VIEW';
return l_id;
end;
begin
dbms_output.put_line(f('USER_OBJECTS'));
end;
> ORA-01422: exact fetch returns more than requested number of rows
-- right way:
declare
function f(object_name in varchar2) return number is
l_id number;
begin
select object_id
into l_id
from all_objects
where object_name = f.object_name -- subprogram_name.parameter_name
and object_type = 'VIEW';
return l_id;
end;
begin
dbms_output.put_line(f('USER_OBJECTS'));
end;
> 12345

Display result of Stored Procedure

I creates a procedure using Toad Client that is
create or replace procedure getuid(eid_pro varchar2)is
l_value number;
begin
select uniqueid
into l_value
from enrollment
where eid=eid_pro;
end ;
When I execute it by Followings
begin
getuid('245698154');
end;
It executed successfully but result is not displayed in data grid.
Please help me in this
Turn the procedure into a function.
create or replace function getuid(eid_pro varchar2) return number is
l_value number;
begin
select uniqueid
into l_value
from enrollment
where eid = eid_pro;
return l_value;
end;
And then select the function.
select getuid('245698154') from dual;
In that case please use a function and then you can use it in a select statement which can show the result in the grid.
create or replace function getuid(eid_pro varchar2) return number is
l_value number;
begin
select uniqueid
into l_value
from enrollment
where eid=eid_pro;
return l_value;
end ;
select getuid('245698154') from dual;

Resources