Dynamic SQL in Informatica - oracle

I have a mapping that is executing dynamic sql via a SQL transformation. The dynamic sql is stored in an Oracle table and I need to add a string parameter to this query.
In this example query, the values compared to COL4 and COL5 are output correctly in the log, but the value compared to COL6 is output as '$$STRING_PARA' in the query -
'SELECT COL1, COL2, COL3 FROM TABLE
WHERE COL4 = $$NUMERIC_PARA
AND COL5 = ''Y''
AND COL6 = ''||$$STRING_PARA||'' '
$$STRING_PARA is being output correctly at the beginning of the log. I have tried omitting the pipes and increasing the number of quotes but nothing seems to work.
Has anyone done something similar?

I guess you want to run the query by replacing the Informatica input variables with the values of the mapping. It might work like this
' SELECT COL1, COL2, COL3 FROM TABLE
WHERE COL4 = $$NUMERIC_PARA
AND COL5 = ''Y''
AND COL6 = ''$$STRING_PARA'' '
In PL/SQL
SQL> set serveroutput on size unlimited echo on lines 200
SQL> #test.sql
SQL> declare
2 v_query varchar2(4000) := ' SELECT COL1, COL2, COL3 FROM TABLE
3 WHERE COL4 = $$NUMERIC_PARA
4 AND COL5 = ''Y''
5 AND COL6 = ''$$STRING_PARA'' ' ;
6 begin
7 dbms_output.put_line(v_query);
8 end;
9 /
SELECT COL1, COL2, COL3 FROM TABLE
WHERE COL4 = $$NUMERIC_PARA
AND COL5 = 'Y'
AND COL6 = '$$STRING_PARA'
PL/SQL procedure successfully completed.
Runtime replacement and execution by replacing input variables
SQL> select object_id, object_name from dba_objects where object_name = 'MY_TEST' ;
OBJECT_ID OBJECT_NAME
---------- --------------------------------------------------------------------------------------------------------------------------------
8897475 MY_TEST
SQL> #test.sql 8897475 MY_TEST
SQL> declare
2 v_query varchar2(4000) := ' SELECT count(*) from dba_objects
3 WHERE object_id = &1
4 AND object_name = ''&2'' ' ;
5 v_counter pls_integer;
6 begin
7 dbms_output.put_line(v_query);
8 execute immediate v_query into v_counter;
9 dbms_output.put_line('Counter is '||v_counter||' ');
10 end;
11 /
old 3: WHERE object_id = &1
new 3: WHERE object_id = 8897475
old 4: AND object_name = ''&2'' ' ;
new 4: AND object_name = ''MY_TEST'' ' ;
SELECT count(*) from dba_objects
WHERE object_id = 8897475
AND object_name = 'MY_TEST'
Counter is 1
PL/SQL procedure successfully completed.
SQL>
It should work the same way, as the escaping for the single quote should be the same.

Thanks for the suggestions - ''$$STRING_PARA'' worked.
I couldn't see that at the time as there was another issue in the mapping that prevented it from working correctly.

Related

Table not found at cursor%NOTFOUND Oracle

I was asked to migrate this code from Sql server
It's used to count all rows with a cursor in a database and we need it to work both on sql server and oracle. The sql server part is working fine, but the oracle part i can't figure out.
DECLARE #tablename VARCHAR(500)
DECLARE #rowname VARCHAR(500)
DECLARE #sql AS NVARCHAR(1000)
IF OBJECT_ID('tempdb..#Temp1') IS NOT NULL
DROP TABLE #Temp1
CREATE TABLE #Temp1
(
tablename varchar(max) ,
rowCounter NUMERIC(38) ,
idColumn varchar(max),
SumIdCol NUMERIC(38)
);
DECLARE db_cursor CURSOR FOR
SELECT s.name + '.' + o.name as name, c.COLUMN_NAME
FROM sys.all_objects o
join sys.schemas s on o.schema_id=s.schema_id
join INFORMATION_SCHEMA.COLUMNS c on o.name=c.TABLE_NAME
WHERE type = 'U' and s.name not like 'sys' and c.ORDINAL_POSITION=1 and c.DATA_TYPE='int'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #tablename,#rowname
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql =
'
insert into #Temp1 values('+
''''+#tablename+''','+
'(SELECT count(1) FROM '+#tablename+' ),'+
''''+#rowname+''''+','+
'(SELECT SUM(['+#rowname+']) FROM '+#tablename+' )'+
')'
EXEC sp_executesql #sql
FETCH NEXT FROM db_cursor INTO #tablename,#rowname
END
CLOSE db_cursor
DEALLOCATE db_cursor
select * from #Temp1
IF OBJECT_ID('tempdb..#Temp1') IS NOT NULL
DROP TABLE #Temp1
Into oracle. I decided to take on PL/SQL and wrote this:
DECLARE
v_tablename varchar2(400);
v_rowname varchar2(400);
cursor db_cursor is
select ALL_OBJECTS.OWNER || '.' || USER_TABLES.TABLE_NAME AS Tablename, USER_TAB_COLS.COLUMN_NAME AS columnName
from USER_TABLES
inner join user_tab_cols ON USER_TAB_COLS.TABLE_NAME=USER_TABLES.TABLE_NAME
inner join ALL_OBJECTS ON user_tab_cols.TABLE_NAME=ALL_OBJECTS.OBJECT_NAME
WHERE ALL_OBJECTS.OBJECT_TYPE = 'TABLE' AND USER_TAB_COLS.COLUMN_ID = 1;
begin
open db_cursor;
loop
fetch db_cursor into v_tablename,v_rowname;
EXIT WHEN db_cursor%NOTFOUND;
execute immediate 'INSERT INTO Temp1(tablename,rowCounter,idColumn,SumIdCol)
SELECT ''' || v_tablename || ''' ,COUNT(*), ''' || v_rowname ||''',SUM(''' || v_rowname ||''') FROM ' || v_tablename;
end loop;
CLOSE db_cursor;
END;
The issue i have is that i get Invalid table or view at line 42 (This line is EXIT WHEN db_cursor%NOTFOUND;)
I know this solution isn't the best but any help would be appreciated. Thanks
I'm not quite sure what are the last two columns in the temp1 table supposed to contain; the first column (why?) and some "sum" value, but - which one?
Anyway: in Oracle, you might start with this code (that compiles and does something; could/should be improved, perhaps):
Target table:
SQL> create table temp1
2 (tablename varchar2(70),
3 rowcounter number,
4 idcolumn varchar2(30),
5 sumidcol number
6 );
Table created.
Anonymous PL/SQL block; note that you need just all_tables and all_tab_columns as they contain all info you need. If you use all_objects, you have to filter tables only (so, why use it at all?).
As all_ views contain all tables/columns you have access to, I filtered only tables owned by user SCOTT because - if I left them all, I'd get various owners (such as SYS and SYSTEM - do you need them too?) and 850 tables.
SQL> select count(distinct owner) cnt_owner, count(*) cnt_tables
2 from all_tables;
CNT_OWNER CNT_TABLES
---------- ----------
19 850
SQL>
Maybe you'd actually want to use user_ views instead.
This code lists all tables, the first column and number of rows in each table:
SQL> declare
2 l_cnt number;
3 begin
4 for cur_r in (select a.owner,
5 a.table_name,
6 c.column_name
7 from all_tables a join all_tab_columns c on c.owner = a.owner
8 and c.table_name = a.table_name
9 where c.column_id = 1
10 and a.owner in ('SCOTT')
11 )
12 loop
13 execute immediate 'select count(*) from ' || cur_r.owner ||'.'||
14 '"' || cur_r.table_name ||'"' into l_cnt;
15
16 insert into temp1 (tablename, rowcounter, idcolumn, sumidcol)
17 values (cur_r.owner ||'.'||cur_r.table_name, l_cnt, cur_r.column_name, null);
18 end loop;
19 end;
20 /
PL/SQL procedure successfully completed.
Result is then:
SQL> select * from temp1 where rownum <= 10;
TABLENAME ROWCOUNTER IDCOLUMN SUMIDCOL
----------------------------------- ---------- --------------- ----------
SCOTT.ACTIVE_YEAR 1 YEAR
SCOTT.BONUS 0 ENAME
SCOTT.COPY_DEPARTMENTS 1 DEPARTMENT_ID
SCOTT.DAT 0 DA
SCOTT.DEPARTMENTS 1 DEPARTMENT_ID
SCOTT.DEPT 4 DEPTNO
SCOTT.DEPT_BACKUP 4 DEPTNO
SCOTT.EMP 14 EMPNO
SCOTT.EMPLOYEE 4 EMP_ID
SCOTT.EMPLOYEES 9 EMPLOYEE_ID
10 rows selected.
SQL>
You are getting the Table not found exception because you have lower-case table names and you are using unquoted identifiers, which Oracle will implicitly convert to upper-case and then because they are upper- and not lower-case they are not found.
You need to use quoted identifiers so that Oracle respects the case-sensitivity in the table names. You can also use an implicit cursor and can pass the values using bind variables (where possible) rather than using string concatenation:
BEGIN
FOR rw IN (SELECT t.owner,
t.table_name,
c.column_name
FROM all_tables t
INNER JOIN all_tab_cols c
ON (t.owner = c.owner AND t.table_name = c.table_name)
WHERE t.owner = USER
AND c.column_id = 1)
LOOP
EXECUTE IMMEDIATE 'INSERT INTO Temp1(tablename,rowCounter,idColumn)
SELECT :1, COUNT(*), :2 FROM "' || rw.owner || '"."' || rw.table_name || '"'
USING rw.table_name, rw.column_name;
END LOOP;
END;
/
fiddle

Needed help to totally remove Temp tables oracle procedures - Oracle - PL/SQL

CREATE OR REPLACE PROCEDURE myDemoStoreProc
(
inputVariable1 IN NUMBER DEFAULT 0 ,
v_cursor OUT SYS_REFCURSOR
)
AS
BEGIN
INSERT INTO temptable1(
SELECT DISTINCT FROM TABLE1
WHERE Col1 = 'logic1' );
INSERT INTO temptable2(
SELECT col2 ,
NVL(( SELECT col1
FROM temptable1 tt1
WHERE sm.col1 = tt1.col1), 0) col3,
col4
FROM table2 sm);
DELETE temptable2
WHERE col4 IN ( 'logic2','logic3' )
OR col4 IS NULL;
IF NVL(inputVariable1 , 0) = 1 THEN
DELETE temptable2
WHERE col1!= 'logic4';
END IF;
OPEN v_cursor FOR
SELECT col1,
col2,
col3,
col4
FROM temptable2;
DBMS_SQL.RETURN_RESULT(v_cursor) ;
END;
As you can see , that there is two temp tables getting used in this stored procedure, how I can remove the dependency of temp tables, can rewrite the whole stored procedure without temp tables
I don't need full code, maybe a pseudo code to adjust the last delete and If logic.
I was trying like a big select query but its not very convenient to do so.
Try below query and see if able to achieve same functionality :
IF NVL(inputVariable1 , 0) = 1 THEN
OPEN v_cursor FOR
select col1,col2,NVL(col1,0) col3,col4
from TABLE1
JOIN TABLE 2
ON TABLE2.col1=TABLE1.COL1
AND (TABLE2.COL4 NOT IN ('logic2','logic3') OR TABLE2.COL4 IS NOT NULL)
AND col1!= 'logic4';
DBMS_SQL.RETURN_RESULT(v_cursor) ;
END IF;

Oracle view: how to obtain a column definition

I have an Oracle view:
create view schemaName.viewName as
select case when 1=1 then 1 else 2 end as col1, decode('A','A','B','C') as col2 from dual
Is there a way to obtain an output or a table with this information:
Column_Name: Col1
Column_Definition: case when 1=1 then 1 else 2 end
Column_Name: Col2
Column_Definition: decode('A','A','B','C')
Thank you very much
Here is the below anonymous block to get column name and column definition for above view.
DECLARE
a varchar2(1000);
b varchar2(1000);
c varchar2(1000);
BEGIN
EXECUTE IMMEDIATE 'SELECT TEXT FROM user_views
WHERE VIEW_NAME=''TEST_VW'' ' INTO a;
SELECT regexp_replace(regexp_substr(a, '^[^,]*'),'select|as\W','') -- getting first argument case to get "case when 1=1 then 1 else 2 end col1"
into b from dual;
DBMS_OUTPUT.PUT_LINE( 'Column_Name: '|| regexp_substr(b,'[^ ]+$')); -- this command to get last col1 from "case when 1=1 then 1 else 2 end col1"
DBMS_OUTPUT.PUT_LINE('Column_Definition: '|| regexp_replace(b,'[^ ]+$','')); -- this command to make col1 to empty to get column definition
select regexp_replace(substr(a,length(regexp_substr(a, '^[^,]*'))+2),'select|dual|from|as\W','') into c from dual; -- this command to get 2nd column "decode('A','A','B','C') col2"
DBMS_OUTPUT.PUT_LINE( 'Column_Name: '|| REGEXP_SUBSTR ( c , '[^ ]+' , 1 , 2 )); -- This command will get 2nd column col2 from "decode('A','A','B','C') col2"
DBMS_OUTPUT.PUT_LINE( 'Column_Definition: '|| REGEXP_SUBSTR ( c , '[^ ]+' , 1 , 1 )); -- This command will get 1st column decode('A','A','B','C') from "decode('A','A','B','C') col2"
END;
Thanks for clarifying ... just try below block and see if this helps ..Also let me know for any column definition which is incorrect
prerequiste for ananymous block:
create view TEST_VW as
select case when 1=1 then 1 else 2 end as col1, decode('A','A','B','C') as col2,
null as col3 , sysdate as col4 , 'var1' as col5
from dual;
-- In view definition I hope a format of each column as and separator of each columns is key to separate the column name and definition for this block.
create sequence seq
start with 1;
create table col_nm_def
(column_nm varchar2(1000),
column_def varchar2(1000),
id number);
Ananymous block;
DECLARE
a varchar2(1000);
b varchar2(1000);
c varchar2(1000);
d number;
BEGIN
EXECUTE IMMEDIATE 'SELECT TEXT FROM user_views
WHERE VIEW_NAME=''TEST_VW'' ' INTO a;
EXECUTE IMMEDIATE 'delete from col_nm_def';
EXECUTE IMMEDIATE 'drop sequence seq';
EXECUTE IMMEDIATE 'create sequence seq
start with 1';
select regexp_replace(regexp_replace(a,'select|from|dual',''),'as\W|, ' , '|') into b from dual; -- replacing as and (, ) to | symbol (try to change symbol if view uses this pipe in transformation)
select regexp_count(b,'[|]') into d from dual;
d:=d+1;
For i in 1..d
LOOP
insert into col_nm_def values (REGEXP_SUBSTR ( b , '[^|]+' , i+1 , i+1 ),REGEXP_SUBSTR ( b , '[^|]+' , i , i ),seq.nextval);
COMMIT;
END LOOP;
END;
Final result : select only odd rows
select * from col_nm_def where mod(id,2)=1 order by ID;

How to include column names along with rows dynamically in Oracle

I am trying to insert the column header and corresponding field into another table .
Table1 :
col1 col2 col3 col4
1 2 3 4
output should look like this:
COL_A COL_B COL_C COL_D COL_E COL_F COL_G COL_H
col1 1 col2 2 col3 3 col4 4
Table1 will be created dynamically , so structure of Table 1 might vary ,for eg .
sometime ,It might be
Case1:
col1 col2 col3 col4
1 2 3 4
For Case1 ,output should look like this:
COL_A COL_B COL_C COL_D COL_E COL_F COL_G COL_H
col1 1 col2 2 col3 3 col4 4
Or it might be
Case2:
col1 col2
1 2
For Case2 ,output should look like this:
COL_A COL_B COL_C COL_D
col1 1 col2 2
I got help with the static query for similar requirement in another question in Stack Overflow,but I was trying to convert the static query to dynamic query.
Static Query :
Static Table structure :
CREATE TABLE Table1
(col1 int, col2 int, col3 int, col4 int)
;
INSERT ALL
INTO Table1 (col1, col2, col3, col4)
VALUES (1, 2, 3, 4)
SELECT * FROM dual
;
Static Query :
SELECT * FROM
(
SELECT *
FROM Table1
UNPIVOT(val FOR col IN (
COL1
,COL2
,COL3
,COL4
))
)
PIVOT( MAX(COl) as C, MAX(VAL) as V FOR COL IN (
'COL1' as VAL1
,'COL2' as VAL2
,'COL3' as VAL3
,'COL4' as VAL4
));
Output :
VAL1_C VAL1_V VAL2_C VAL2_V VAL3_C VAL3_V VAL4_C VAL4_V
COL1 1 COL2 2 COL3 3 COL4 4
As I tried to make the static query dynamic ,I tried this :
SELECT * FROM
(
SELECT *
FROM Table1
UNPIVOT(val FOR col IN (
select regexp_substr((select * from (SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TABLE1'))),'[^,]+', 1, level) "yyy" from dual
connect by regexp_substr((select * from (SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TABLE1'))), '[^,]+', 1, level) is not null
))
)
PIVOT( MAX(COl) as C, MAX(VAL) as V FOR COL IN (
select regexp_substr((select '''' || AA ||'''' from (SELECT LISTAGG(COLUMN_NAME, ''',''') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TABLE1'))),'[^,]+', 1, level) "yyy" from dual
connect by regexp_substr((select '''' || AA ||'''' from (SELECT LISTAGG(COLUMN_NAME, ''',''') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TABLE1'))), '[^,]+', 1, level) is not null
));
But this throws error :
ORA-00904: : invalid identifier
00904. 00000 - "%s: invalid identifier"
*Cause:
*Action:
Error at Line: 6 Column: 5
I tried using dynamic sql .Below is the dynamic sql I used :
declare
sql_stmt varchar2(4000);
pivot_clause1 varchar2(4000);
pivot_clause2 varchar2(4000);
begin
SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP ( order by COLUMN_ID) aa
into pivot_clause1
FROM (select * from all_tab_columns where table_name = 'TEST11') ;
select * into pivot_clause2 from
(
select AA ||'''' aa
from (SELECT ''''||LISTAGG(COLUMN_NAME, ''',''') WITHIN GROUP ( order by COLUMN_ID) aa
FROM (select * from all_tab_columns where table_name = 'TEST11'))
);
dbms_output.put_line(pivot_clause1);
dbms_output.put_line(pivot_clause2);
sql_stmt := 'create table test13 as ('|| 'SELECT * FROM
(
SELECT *
FROM TEST11
UNPIVOT(val FOR col IN ( '
|| pivot_clause1 || '
))
)
PIVOT( MAX(COl) as C, MAX(VAL) as V FOR COL IN (' ||
pivot_clause2 || '
))
)';
execute immediate sql_stmt ;
end;
/
It is working fine for a table having description as below :
Name Null Type
---- ---- ----------
COL1 NUMBER(38)
COL2 NUMBER(38)
COL3 NUMBER(38)
COL4 NUMBER(38)
But it is not working for a table having description :
COL1 DATE
COL1 VARCHAR2(100)
COL1 VARCHAR2(100)
COL1 NUMBER
COL1 NUMBER
I am getting below error :
Error report -
ORA-01790: expression must have same datatype as corresponding expression
ORA-06512: at line 38
01790. 00000 - "expression must have same datatype as corresponding expression"
*Cause:
*Action:
I just learned unpivot cannot be used with columns having different datatype,Is there any way around ?
Can someone please help !!

If statement inside create table query -Oracle-

I would like to create table which asks user input first. Then based on the input, it select which columns are added.
for example, if the response is 'N', then table is created including columns col1, col2, col3.
If the response is 'Y', table is created including columns col1, col2, col3, col4, col5.
Is this possible?
If yes, please provide me simple and primitive query so that I can apply it to my case.
Thanks,
Using SQL*Plus it's simple:
ACCEPT table_option -
PROMPT 'Create more columns? '
SET TERM OFF
COLUMN extra_columns NEW_VALUE extra_columns
SELECT
CASE '&table_option'
WHEN 'Y' THEN ', C4 NUMBER, C5 VARCHAR2(255), C6 DATE'
END extra_columns FROM DUAL;
CREATE TABLE tmp (
C1 NUMBER,
C2 VARCHAR2(255),
C3 DATE &extra_columns
);
SET TERM ON
You can store the script as a file and invoke it from SQL*Plus using #filename.
CREATE OR REPLACE FUNCTION tmp_custom_DDL( p_input VARCHAR2 IN, p_resp CHAR IN OUT) RETURN CHAR
AS
v_str VARCHAR2(4000);
IF p_resp = 'Y' THEN
v_str := 'col1 varchar2(10), col2 varchar2(10), col3 varchar2(10)';
ELSE v_str := 'col1 varchar2(10), col2 varchar2(10), col3 varchar2(10), col4 varchar2(10), col4 varchar2(10) ' ;
EXECUTE IMMEDIATE v_comm_1 || v_str || v_comm2;
--v_comm_1 is the first half of create table command till the specified cols
--v_comm_2 is the rest of the create table command
RETURN p_resp;
END;
this is only a quick draft, fix the few lexical bug and the missing definitions :) (this is the first step)

Resources