Passing in parameter for a select alias - oracle

In a Pl sql function in need to pass in varchar parameter for a select statement alias. For example something like below,
create or replace
FUNCTION XX_FUNC_TEST (p_id in varchar2 , p_app_type in varchar2) RETURN varchar2 IS
appl_type_r varchar2(20);
BEGIN
SELECT p_app_type
INTO appl_type_r
FROM YY_TABLE
WHERE trans_id = p_id;
dbms_output.put_line(appl_type_r);
RETURN appl_type_r;
END XX_FUNC_TEST;
So its like the real column name is applicant_type which i pass as a parameter. But some how the out put i get is applicant_type which i pass as the parameter not the column value.

This is because you need to create your query dynamically:
CREATE OR REPLACE FUNCTION XX_FUNC_TEST (p_id in varchar2 , p_app_type in varchar2) RETURN VARCHAR2 IS
appl_type_r VARCHAR2(20);
sql VARCHAR2(2000);
BEGIN
sql :=
'SELECT ' || p_app_type || ' ' ||
'FROM YY_TABLE ' ||
'WHERE trans_id = p_id';
EXECUTE IMMEDIATE sql INTO appl_type_r;
DBMS_OUTPUT.PUT_LINE(appl_type_r);
RETURN appl_type_r;
END XX_FUNC_TEST;
Otherwise, Oracle thinks you just want to select the value itself.

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

How can I make table name from two string column?

I need to make a PL/SQL script.
The inputs are a schema name and a table name. How can I make it to a table name?
So e.g. I'd like to do this:
create or replace procedure proc(schema in varchar2, table in varchar2) is
begin
select * from 'schema.table';
end;
begin
proc('db', 'items');
end;
So I'd like to get everything from db.items.
I've tried concat, ( 'schema' || '.' || 'table'), put it in a variable, but non of these has worked.
What you need is dynamic sql. Example that will return and print the count of rows (you can change it accordingly to your needs):
SQL> set serveroutput on -- to be able to see the printed results.
SQL> create or replace procedure proc(p_schema in varchar2, p_table in varchar2) is
v_sql varchar2(100);
v_result number;
begin
v_sql := 'select count(*) from :1' || '.' || ':2';
EXECUTE IMMEDIATE v_sql into v_result USING p_schema, p_table;
DBMS_OUTPUT.PUT_LINE ('Total rows in table: '|| v_result );
end;

how to store and call a sql query from a table column?

Insert statement below holds details of another Table that has 3 columns (id,ins_dt,text_stuff)
INSERT INTO swpurge_config
(schema
,table_name
,table_alias
,driving_column
,deletion_predicate
,retention_period_type
,retention_period_value)
VALUES
('CUSTOMERS_OWNER'
,'LAST_NAMES'
,'LN'
,'ins_dt'
,'WHERE ln.ins_dt < SYSDATE - p_retention_period_value
AND ora_hash(ln.rowid, 8) = 1'
,'month'
,'6');
Aim:
I am essentially trying to add a delete predicate in a varchar2 column. The idea is to call this column in a Procedure which will delete records up to 1000 rows:
PROCEDURE delete_rows
(
p_schema IN VARCHAR2
,p_table_name IN VARCHAR2
,p_table_alias in varchar2
,p_retention_period_value IN VARCHAR2
,p_delete_predicate IN VARCHAR2
) IS
v_sql varchar2 (32000);
v_row_limit pls_integer (1000);
BEGIN
v_sql := 'delete from ' || p_schema ||'.'|| table_name ||p_table_alias
'older than '|| p_retention_period_value || p_delete_predicate;
dbms_output.put_line(v_sql);
END delete_rows;
Not sure about 2 things:
1. How to store the sql where clause in a table column?
2. How to execute the where clause as a statement in the procedure?
Thanks
you are talking about Dynamic SQL.
You can just store it inside VARCHAR2 in string format. then retrieve it
select deletion_predicate into v_sql from swpurge_config where ...
then
v_sql := "SELECT ...... FRom .... " || v_sql;
finally execute it
EXECUTE IMMEDIATE v_sql;
if your sql statement also includes parameters then
EXECUTE IMMEDIATE query_str
using param1;

How to dynamically pass select query to a procedure/function and get the record count using ref cursor?

FUNCTION prc_check_src_dest_cnt (p_source IN SYS_REFCURSOR)
RETURN VARCHAR2
IS
p_source -- how to find the count of this cursor which is dynamically changing query passed by user?
If you can pass SQL Query text instead, you can try the below,as Alex pointed out in the comments
Create or replace function FUN_REF_COUNT(IN_SQL varchar2)
RETURN number
AS
v_count number;
Begin
execute immediate 'select count(*) from (' || IN_SQL || ' )'
into v_count;
return v_count;
end;
/
Calling Function:
select FUN_REF_COUNT('select * from all_tables') FUNCTION_OUTPUT
from dual ;
--Output
!----------!-----!
FUNCTION_OUTPUT
1697

Oracle Create Function

I tried to create an Oracle function where table name, column name and where string are dynamic parameters:
CREATE OR REPLACE FUNCTION MYSCHEMA.myFunctionName(
tableName in nvarchar2,
columnName in nvarchar2,
whereStr in nvarchar2)
RETURN nvarchar2
IS nActive nvarchar2(2000);
BEGIN
declare
querystr nvarchar2(2000) ;
result nvarchar2(2000);
begin
querystr :='
select listagg('+columnName+','+','+') within group (order by '+columnName+')
from '+tableName+' where 1 = 1 '+whereStr+';';
EXECUTE IMMEDIATE querystr
INTO result;
nActive := result;
RETURN ( nActive );
end;
END ;
/
But it gives me error "Warning: compiled but with compilation errors".
What am I doing wrong?
For concatenate strings in Oracle use || not +
You don't need ; at the end of execute immediate query string
You need to escape ' using ''.
... as #Aleksej said
Execute immediate need query string in CHAR or VARCHAR2;
listagg return either raw or VARCHAR2
CREATE OR REPLACE FUNCTION MYSCHEMA.myFunctionName(
tableName in varchar2,
columnName in varchar2,
whereStr in varchar2)
RETURN varchar2
BEGIN
declare
querystr varchar2(2000) ;
result varchar2(2000);
begin
querystr :='
select listagg('|| columnName || ', '','') within group (order by ' ||columnName ||')
from ' || tableName || ' where 1 = 1 ' || whereStr;
EXECUTE IMMEDIATE querystr INTO result;
return result;
end;
END ;
/
You need to change the type of your variables, considering that
"The return data type is RAW if the measure column is RAW; otherwise the return value is VARCHAR2" (documentation)
EXECUTE needs a VARCHAR2: "It must be of type CHAR or VARCHAR2, not NCHAR or NVARCHAR2"
So:
declare
querystr varchar2(2000) ;
result VARCHAR2(32767);
begin
...

Resources