Create Master SP in Teradata to call other SP dynamically - sql

REPLACE PROCEDURE PD_CCP_FN_DB.master_proc_TEST()
SQL SECURITY INVOKER
BEGIN
DECLARE STMT VARCHAR(4000);
FOR rec AS Cur CURSOR FOR
select SP_NAME from WRK_QA_CCP_ATOMIC_DB.SP_CALL
DO
SET STMT= ' CALL ' || rec.SP_NAME || '(''PII_SUPP_SMRY_TXN'') ';
EXECUTE IMMEDIATE STMT;
END FOR;
END;

Related

Dynamic loop in PL/SQL

I am currently looping through values in PL/SQL with the following:
for c in (select * from example_table where name is not null) loop
-- logic
end loop;
I would like to replace the SQL statement with a dynamic one, for example:
l_sql := 'select * from example_table where || l_col || is not null';
for c in (l_sql) loop
-- logic
end loop;
Is this possible?
Best regards
That's not possible with an implicit cursor loop ( select inside for loop ). You may use the conventional OPEN .. FETCH .. LOOP through a REFCURSOR with a record variable of tablename%ROWTYPE
DECLARE
t_rec example_table%ROWTYPE;
l_sql VARCHAR2(1000);
v_cur SYS_REFCURSOR;
l_col varchar2(32) := 'MY_COLUMN';
BEGIN
l_sql := 'select * from example_table where '|| l_col || ' is not null';
OPEN v_cur FOR l_sql;
LOOP
FETCH v_cur INTO t_rec; --fetch a row
EXIT WHEN v_cur%NOTFOUND;
-- your logic using t_rec columns.
END LOOP;
CLOSE v_cur;
END;
/

Creating dynamic SQL select from table columns

I need a to create an sql script which from a table table will build a dynamic sql request in the following way:
select ID || ',' || NAME || ',' || CODE
from TABLE_TEST;
The table name is received as an input parameter.
My SQL script is as follows;
spool exportTable.log
SET HEAD OFF
SET TRIMOUT ON
SET TRIMSPOOL ON
SET LINESIZE 32000
SET PAGESIZE 0
SET TERMOUT OFF
SET ECHO OFF
SET COLSEP ,
procedure CreerReq(sTable in Varchar) is
dbms_output.put_line('dans CreerReq');
sqlreq VARCHAR2(2000);
sChaine VARCHAR2(4000):='select';
TYPE t_tab IS TABLE OF VARCHAR2(4000);
l_tab t_tab;
l_tab_Id t_tab;
l_ref_cursor SYS_REFCURSOR;
dbms_output.put_line(sChaine);
begin
sqlreq := 'select column_name from all_tab_cols WHERE table_name' || sTable;
dbms_output.put_line(sqlreq);
OPEN l_ref_cursor FOR sqlreq;
LOOP
FETCH l_ref_cursor BULK COLLECT INTO l_tab_Id,l_tab limit 1000;
EXIT WHEN l_tab.COUNT=0;
FOR i IN l_tab.first .. l_tab.last LOOP
sChaine:=l_tab(i) ' || ','';
END LOOP;
CLOSE l_ref_cursor;
dbms_output.put_line(sChaine);
End CreerReq;
BEGIN
dbms_output.put_line('&1');
CreerReq(&1);
END;
/
spool off;
However this is returning me the following error:
ORA-00900: invalid SQL statement
Any help please?
CREATE or replace FUNCTION build_select (
p_owner IN VARCHAR2
, p_table_name IN VARCHAR2
)
RETURN VARCHAR2
AS
l_ret VARCHAR2 (32767);
BEGIN
FOR eachcol IN ( SELECT column_name
, LEAD (column_name)
OVER (
PARTITION BY table_name ORDER BY column_id
)
next_column
FROM all_tab_cols
WHERE owner = p_owner
AND table_name = p_table_name
ORDER BY column_id)
LOOP
l_ret := l_ret || eachcol.column_name || CASE WHEN eachcol.next_column IS NULL THEN NULL ELSE ',' END;
END LOOP;
IF l_ret IS NULL
THEN
raise_application_error (-20001, 'table ' || p_owner || '.' || p_table_name || ' not found');
END IF;
l_ret := 'select ' || l_ret || ' from ' || p_owner || '.' || p_table_name;
RETURN l_ret;
END build_select;
So let's test it out:
begin dbms_output.put_line(build_select('SYS', 'ALL_TAB_COLS')); end;
Results in:
select OWNER,TABLE_NAME,COLUMN_NAME,DATA_TYPE,DATA_TYPE_MOD,DATA_TYPE_OWNER,DATA_LENGTH,DATA_PRECISION,DATA_SCALE,NULLABLE,COLUMN_ID,DEFAULT_LENGTH,DATA_DEFAULT,NUM_DISTINCT,LOW_VALUE,HIGH_VALUE,DENSITY,NUM_NULLS,NUM_BUCKETS,LAST_ANALYZED,SAMPLE_SIZE,CHARACTER_SET_NAME,CHAR_COL_DECL_LENGTH,GLOBAL_STATS,USER_STATS,AVG_COL_LEN,CHAR_LENGTH,CHAR_USED,V80_FMT_IMAGE,DATA_UPGRADED,HIDDEN_COLUMN,VIRTUAL_COLUMN,SEGMENT_COLUMN_ID,INTERNAL_COLUMN_ID,HISTOGRAM,QUALIFIED_COL_NAME from SYS.ALL_TAB_COLS
Here is a simpler build_select function:
CREATE OR REPLACE FUNCTION build_select (
p_owner IN VARCHAR2
, p_table_name IN VARCHAR2
)
RETURN VARCHAR2
AS
l_ret VARCHAR2 (32767);
BEGIN
SELECT
LISTAGG (column_name, ',') WITHIN GROUP (ORDER BY column_id)
INTO l_ret
FROM all_tab_cols
WHERE table_name = p_table_name
AND owner = p_owner;
IF l_ret IS NULL
THEN
raise_application_error (-20001, 'table ' || p_owner || '.' || p_table_name || ' not found');
END IF;
l_ret := 'select ' || l_ret || ' from ' || p_owner || '.' || p_table_name;
RETURN l_ret;
END build_select;
I haven't worked with oracle in a while. But on sql server this is how it can be done. Look through this code and it might point you to the right direction:
/* This stored procedure builds dynamic SQL and executes
using sp_executesql */
Create Procedure sp_EmployeeSelect
/* Input Parameters */
#EmployeeName NVarchar(100),
#Department NVarchar(50),
#Designation NVarchar(50),
#StartDate DateTime,
#EndDate DateTime,
#Salary Decimal(10,2)
AS
Set NoCount ON
/* Variable Declaration */
Declare #SQLQuery AS NVarchar(4000)
Declare #ParamDefinition AS NVarchar(2000)
/* Build the Transact-SQL String with the input parameters */
Set #SQLQuery = 'Select * From tblEmployees where (1=1) '
/* check for the condition and build the WHERE clause accordingly */
If #EmployeeName Is Not Null
Set #SQLQuery = #SQLQuery + ' And (EmployeeName = #EmployeeName)'
If #Department Is Not Null
Set #SQLQuery = #SQLQuery + ' And (Department = #Department)'
If #Designation Is Not Null
Set #SQLQuery = #SQLQuery + ' And (Designation = #Designation)'
If #Salary Is Not Null
Set #SQLQuery = #SQLQuery + ' And (Salary >= #Salary)'
If (#StartDate Is Not Null) AND (#EndDate Is Not Null)
Set #SQLQuery = #SQLQuery + ' And (JoiningDate
BETWEEN #StartDate AND #EndDate)'
/* Specify Parameter Format for all input parameters included
in the stmt */
Set #ParamDefinition = ' #EmployeeName NVarchar(100),
#Department NVarchar(50),
#Designation NVarchar(50),
#StartDate DateTime,
#EndDate DateTime,
#Salary Decimal(10,2)'
/* Execute the Transact-SQL String with all parameter value's
Using sp_executesql Command */
Execute sp_Executesql #SQLQuery,
#ParamDefinition,
#EmployeeName,
#Department,
#Designation,
#StartDate,
#EndDate,
#Salary
If ##ERROR <> 0 GoTo ErrorHandler
Set NoCount OFF
Return(0)
ErrorHandler:
Return(##ERROR)
GO

Dynamic cursors and WHERE clause Teradata

I'm trying to write a procedure where the databasename is dynamic and is taken from a where clause.
So far I have got this:
CREATE PROCEDURE Test
(IN DBName VARCHAR(100), OUT RowCount DEC(10,2))
BEGIN
DECLARE SqlStr VARCHAR(1000);
DECLARE C1 CURSOR FOR S1;
SET SqlStr = 'SELECT count(*) FROM '|| DBNAME || '.MyTable ';
PREPARE S1 FROM SqlStr;
OPEN C1 USING DBName;
FETCH C1 INTO RowCount;
CLOSE C1;
END;
I would need to add something like this now:
WHERE DBName = (SELECT 'firstpart||EnvName||' FROM EnvTable
WHERE EnvName = (SELECT EnvName FROM EnvTable WHERE Flag = 1 AND Priority = (SELECT MIN(Priority) FROM EnvTable))
Any ideas? Can I add this when I call the procedure?
Sounds like you just need a variable to make this more dynamic:
CREATE PROCEDURE Test
(OUT RowCount DEC(10,2))
BEGIN
DECLARE SqlStr VARCHAR(1000);
DECLARE DBName VARCHAR(100);
DECLARE C1 CURSOR FOR S1;
/*Get your DBName variable loaded using SELECT INTO*/
SELECT 'firstpart' || EnvName INTO DBName
FROM EnvTable
WHERE Flag = 1 AND Priority = (SELECT MIN(Priority) FROM EnvTable);
/*and continue what you were doing*/
SET SqlStr = 'SELECT count(*) FROM '|| DBName || '.MyTable ';
PREPARE S1 FROM SqlStr;
OPEN C1 USING DBName;
FETCH C1 INTO RowCount;
CLOSE C1;
END;
I'm not sure what you were trying to do with firstpart||envname, but this should get you in the ballpark. Essentially you just need to craft a sql statement that makes your dbname variable and then use that in your second query.

Replace apostrophe in a dynamically created insert statement

I have created a lookup table which holds Insert statements. Some of the values in the values have apostrophes in them.
The value appears like this
'SCRW, PAN-HD PHIL, THR'D: 8-32. L: 3/8""'
Whole statement is:
INSERT INTO PARTS_BOMD (BOM_ID, ITEM_REV, ITEM_CN, ITEM_NUMBER, ITEMNUMBER, FINDNUM, QTY, ITEMDESCRIPTION, ITEMREV, ITEMSIZE, REFDES, BOMTEXT02, ITEMLIST21, SUMMARYCOMPLIANCE, BOMMULTITEXT30, BOMNOTES, ITEMLIST10, BOMLIST01, BOMLIST03, BOMLIST02, ITEMTEXT22, ITEMTEXT23, ITEMLIFECYCLEPHASE, ITEMP2MULTILIST05, ITEMTEXT15, RNUM) VALUES (2009034062,'31','ECO05447','1472096','1422042','100','4','SCRW, PAN-HD PHIL, THR'D: 8-32. L: 3/8""','A0 MPL03682','PC','PC','','6 out of 6 Compliant','Missing Info','Missing Info','','ZHLB','','','X','','','AREL','Yes','0.10220',582272);
This table have more than 4 million records and this issue occurs most of the time.
Is there any way I can change this apostrophe into two single quotes.
I am using SQL Server 2014.
Oracle scripts which generates these insert statements:
set serveroutput on size 100000
set feedback off
declare
v_table_name varchar2(30) := 'PARTS_BOMD'; -- Your Tablename
v_column_list varchar2(2000);
v_insert_list varchar2(2000);
v_ref_cur_columns varchar2(4000);
v_ref_cur_query varchar2(2000);
v_ref_cur_output varchar2(2000);
v_column_name varchar2(2000);
cursor c1 is select column_name, data_type from user_tab_columns where table_name = v_table_name order by column_id;
refcur sys_refcursor;
begin
for i in c1 loop
v_column_list := v_column_list||','||i.column_name;
if i.data_type = 'NUMBER' then
v_column_name := i.column_name;
elsif i.data_type = 'DATE' then
v_column_name := chr(39)||'to_date('||chr(39)||'||chr(39)'||'||to_char('||i.column_name||','||chr(39)||'dd/mm/yyyy hh:mi:ss'||chr(39)||')||chr(39)||'||chr(39)||', '||chr(39)||'||chr(39)||'||chr(39)||'dd/mm/rrrr hh:mi:ss'||chr(39)||'||chr(39)||'||chr(39)||')'||chr(39);
elsif i.data_type = 'VARCHAR2' then
v_column_name := 'chr(39)||'||i.column_name||'||chr(39)';
end if;
v_ref_cur_columns := v_ref_cur_columns||'||'||chr(39)||','||chr(39)||'||'||v_column_name;
end loop;
v_column_list := ltrim(v_column_list,',');
v_ref_cur_columns := substr(v_ref_cur_columns,8);
v_insert_list := 'INSERT INTO '||v_table_name||' ('||v_column_list||') VALUES ';
v_ref_cur_query := 'SELECT '||v_ref_cur_columns||' FROM '||v_table_name;
open refcur for v_ref_cur_query;
loop
fetch refcur into v_ref_cur_output;
exit when refcur%notfound;
v_ref_cur_output := '('||v_ref_cur_output||');';
v_ref_cur_output := replace(v_ref_cur_output,',,',',null,');
v_ref_cur_output := replace(v_ref_cur_output,'(,','(null,');
v_ref_cur_output := replace(v_ref_cur_output,',,)',',null)');
v_ref_cur_output := replace(v_ref_cur_output,'null,)','null,null)');
v_ref_cur_output := v_insert_list||v_ref_cur_output;
--dbms_output.put_line (v_ref_cur_output);
INSERT INTO BOM_INS_LOOKUP(LOOKUP_STATMENT)
VALUES (v_ref_cur_output);
COMMIT;
end loop;
end;
/
It ain't much but it does the job.
There really isn't an easy way to approach this problem generically. How would you go about processing a list of values like this one?: 'A',',','B' Is it one or two or three values? And there are two ways to split it into two values.
Here's an approach you might take by making some assumptions about the format.
declare #pos int;
declare #s varchar(1024);
declare myCursor for select <COLUMN> from T;
open myCursor;
while ##fetch_status = 0
begin
fetch next from myCursor into #s;
set #pos = charindex(#s, '''', #pos);
while #pos > 0
begin
/* if apostrophe is followed by comma plus apostrophe
then assume this is the delimiter */
if substring(#s +',''', #pos + 1, 2) <> ','''
set #s = stuff(#s, #pos, 1, '''''');
else
set #pos = #pos + 2;
set #pos = charindex(#s, '''', #pos);
end
update T set <COLUMN> = #s where current of myCursor;
end
close myCursor;
deallocate myCursor;
It would be better to avoid the problem in the first place by properly quoting the values as the INSERT queries are generated. There are many ways to export data from Oracle that can be readily picked up by SQL Server.

How to add table name into dynamic query in execute immediate?

I'm confused, because I don't know how to use variable as string in execute immediate clause.
declare
variable1 varchar2(30):
cur sys_refcursor;
begin
open cur for
select tablename from table1;
loop
fetch cur into variable1;
exit when cur %notfound;
execute immediate 'select count(*)
into variable1
from user_tables
where table_name =''' || variable1 || '''';
end loop;
close cur;
end;
Here variable1 is a table name. There should be a string value. How to do that? Still got an error.
The execute immediate statement should look like this:
execute immediate 'select count(*) from user_tables where table_name ='''||variable1||'''' into variable1;
with the into xxx after the query.