Parameters and teradata Dynamic SQL - stored-procedures

I'm trying to use a parameter within my dynamic sql query, but just can't get it to work. I simplified my code to highlight only the part I'm struggling with.
I first create 't1', to which I insert the record.
create volatile table t1 as(
select date '1900-01-01' as date_col
)with data on commit preserve rows;
First procedure works fine, and insert the given date (2014-01-01).
replace procedure mi_table.dynamic_param()
begin
CALL DBC.SYSEXECSQL(
'insert into t1
select date '||'''2014-01-01'''||' ;'
);
end;
What I really want to do and doesn't work, is something as such:
replace procedure mi_table.dynamic_param()
begin
declare max_avail_date date;
set max_avail_date = (select period_dt from db.table) ;
CALL DBC.SYSEXECSQL(
'insert into t1
select '||:max_avail_date||' ;'
);
end;
which doesn't work. It seems like what is returned from '(select period_dt from db.table)' is under 'yy/mm/dd' format. Thanks to the suggestions below, I came up with the following (non-elegant) solution:
Whenever I want to use max_avail_date, I use
cast((cast('''||'20'||'''||'''||max_avail_date||''' as int)-19000000) as date) .
It does the job, but I'm sure there's a better way of doing it :)
edited 2014-09-27, 6:50pm

"doesn't work" is not a very precise error description :-)
You probably got a "invalid date" message?
The concatenated string in strSQL must be a valid SQL statement, your's resulted in
insert into t1 select date 2014-01-01 ;
This will work:
replace procedure mi_table.dynamic_param() begin
declare max_avail_date date;
set max_avail_date = date '2014-01-01' ;
CALL DBC.SYSEXECSQL(
'insert into t1 select '''||:max_avail_date||''' ;'
);
end;
Edit:
Regarding the issue with the 'yy/mm/dd' format this is probably due to the global default on your system. You better implicitly assign a FORMAT:
CALL DBC.SYSEXECSQL(
'insert into t1 select '''|| (:max_avail_date (FORMAT 'yyyy-mm-dd'))||''' ;'
);

This should work:
declare max_avail_date varchar(10);
declare strSQL varchar(2000);
set max_avail_date = '2014-01-01';
set strSQL = 'insert into t1 select (date''' || max_avail_date || ''')';
CALL DBC.SYSEXECSQL(:strSQL);
I could never get it to work the way you're trying it. This bit here was killing me:
set max_avail_date = date '2014-01-01' ; I think trying to use the cast in the set statement won't work.
Setting it as a string and converting it to a date in the select statement works fine.

Related

Set a variable value in stored procedure and use in following query

How can I set a variable in stored procedure and use it in following query to be executed.
create or replace procedure sp1()
returns table (dealer_id varchar, dealershipgroup_name varchar)
language sql
as
$$
declare
create_query varchar;
res resultset;
MISSING_DEALER NUMBER(38,0) default 0;
begin
MISSING_DEALER := 100;
select_query := 'WITH CTE AS(
SELECT dealer_id,
CASE WHEN dealer_id=:MISSING_DEALER then \'Abc\'
WHEN dealershipgroup IS NULL then \'\'
ELSE dealershipgroup end as dealershipgroup FROM TBL )
select * from CTE';
res:= (execute immediate : select_query);
return table(res);
end;
$$;
call sp1();
Could someone please suggest how can I use MISSING_DEALER in the query. I am currently getting the following error
Uncaught exception of type 'STATEMENT_ERROR' on line 28 at position 9 : SQL compilation error: error line 8 at position 26 Bind variable :MISSING_DEALER not set
You need to concatenate the string parts of your SQL statement with the variable. This is covered in the documentation if you look at the end of the section here

Can I get an Example of a Snowflake Stored Procedure that takes in StartDate and ReturnDate then executes 2 Select statements and returns a table()?

Can I get an Example of a Snowflake Stored Procedure that takes in StartDate and ReturnDate then executes 2 Select statements and returns a table()? It needs to be a WORKING example. Just Table1 and Table2 for table names, etc. I am just looking for a good example syntax wise.
If you want to return a table, you must use a SQL Script stored procedure. I'm not sure what you want the second select to do, so here's a sample with a single select. Are you looking for the second one to key off of something it finds after running the first statement?
create or replace procedure test (start_date date, end_date date)
returns table()
language sql
as
$$
declare
res resultset default (
select *
from "SNOWFLAKE_SAMPLE_DATA"."TPCH_SF1"."ORDERS"
where O_ORDERDATE >= :start_date
and O_ORDERDATE <= :end_date
);
begin
return table(res);
end;
$$;
call test('1994-01-01', '1994-01-02');

provide column data type in DBSMS_SQL.DEFINE_COLUMN oracle 12C

I have to copy data from one table to another with below two conditions
table names will be known at run time
records need to be copied one at a time so that modifications can be done in column values when required
I have created a procedure to to do this through dynamic query. Since the column list is not known already I am not able to declare a rowtype variable. I saw an example of DBMS_SQL where you can define the columns for select clause. Below is the format
DBMS_SQL.DEFINE_COLUMN(cursor_var,position,column_var);
Problem here is that in all the examples I found the column_var were already declared. However in my case I will get to know the no of columns that will be in cursor sql and their data type at run time. so I need to find a way to pass the data type of "column_var" as part of DBMS_SQL.DEFINE_COLUMN. Is there a way to do that? Is there a better way?
Below is just a sample code
CREATE OR REPLACE PROCEDURE pr_test (P_TABLE_NAME IN VARCHAR2)
IS
V_SQL VARCHAR2(500);
SRC_CUR INT;
DEST_CUR INT;
TYPE COL_DTL_TYPE IS RECORD
(
COLUMN_ID INT,
COLUMN_NAME VARCHAR2(250),
DATA_TYPE VARCHAR2(250),
DATA_LENGTH INT
);
COL_DTL_REC COL_DTL_TYPE;
TYPE TBL_COL_LIST_TYPE IS TABLE OF COL_DTL_TYPE;
TBL_COL_LIST TBL_COL_LIST_TYPE;
V_CNT INT := 0;
BEGIN
V_SQL := 'SELECT * FROM ' || P_TABLE_NAME;
SRC_CUR := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(SRC_CUR,V_SQL,DBMS_SQL.NATIVE);
TBL_COL_LIST := TBL_COL_LIST_TYPE();
FOR COL_DTL_REC IN (
SELECT COLUMN_ID,COLUMN_NAME,DATA_TYPE,DATA_LENGTH
FROM ALL_TAB_COLUMNS WHERE TABLE_NAME =P_TABLE_NAME
)
LOOP
V_CNT := V_CNT + 1;
TBL_COL_LIST.EXTEND;
TBL_COL_LIST(V_CNT) := COL_DTL_REC;
-- Here is where I am stuck and not able to give column data type
DBMS_SQL.DEFINE_COLUMN(SRC_CUR,V_CNT,COL_DTL_REC.COLUMN_NAME COL_DTL_REC.DATA_TYPE , COL_DTL_REC.DATA_LENGTH)
END LOOP;
END;
copying to destination table will come later.

Inserting values in to netezza using a stored procedure

I'm working with netezza database and have a requirement to insert a Y flag for stores in California. I wrote the below procedure
CREATE OR REPLACE PROCEDURE MY_NEW_PROCEDURE() RETURNS BOOL
EXECUTE AS OWNER LANGUAGE NZPLSQL AS
BEGIN_PROC
DECLARE
rec RECORD;
BEGIN
FOR rec in SELECT * from test_table
LOOP
if rec.state_code ='CA'
EXECUTE IMMEDIATE 'INSERT INTO test_table (california_stores)' || 'values('y')';
END LOOP;
END;
END_PROC;
when I call the procedure using call MY_NEW_PROCEDURE() I get an error at line EXECUTE IMMEDIATE. I'm not sure what change I need to make here.
Don't know about netezza, but below is the procedure I used to test this (using SQL developer).
Works fine for me, although it would make more sense to update the row to set california_stores to 'Y'rather than insert a new row with california_stores = 'Y' for each calafornia store that you have.....
CREATE OR REPLACE PROCEDURE "MY_NEW_PROCEDURE" as
rec test_table2%rowtype;
BEGIN
FOR rec in (SELECT * from test_table2) LOOP
if rec.state_code = 'CA' then
EXECUTE IMMEDIATE 'INSERT INTO test_table2 (california_stores)' || 'values(''y'')';
end if;
END LOOP;
END;
You didn't post the second error, but it looks to me like your insert statement isn't going to do what you want anyway. If the rec variable contains the attribute state_code and you're inserting a single value to test_table then the record will simply be empty except for a 'Y' in california_stores.
I'm going to guess that you're getting an error now either because of the spacing in the insert statement insert into test_table (california_stores)values('y') or because you didn't terminate the execute statement with a semicolon. The plsql for that line should be
execute immediate 'insert into test_table (california_stores) values (''y'');';

How to use procedure parameters in merge statement

i'm creating a procedure to update/insert a table using merge statement(upsert).now i have a problem: using procedure parameters i have to do this upsert.
procedure xyz( a in table.a%type,b in table.b%type,....)
is
some local variables;
begin
merge into target_table
using source_table --instead of the source table, i have to use procedure parameters here
on (condition on primary key in the table)
when matched then
update the table
when not matched then
insert the table ;
end xyz;
so how to use procedure parameters instead of source table in merge statement?? or
suggest me a query to fetch the procedure parameters and use it as source table values.
help me please.
Thanks in advance.
I know that I'm eight years late to the party, but I think that I was trying to do something similar to what you were doing, but trying to Upsert based on parameters passed into a stored procedure that returns an empty string on success and an error on failure back to my VB Code. Below is all of my code along with comments explaining what I did, and why I did it. Let me know if this helps you or anyone else. This is my first time answering a post.
PROCEDURE UpsertTSJobData(ActivitySeq_in IN NUMBER,
Owner_in In VARCHAR2,
NumTrailers_in IN NUMBER,
ReleaseFormReceived_in IN NUMBER,
Response_out OUT VARCHAR2) AS
err_num NUMBER;
err_msg VARCHAR2(4000);
BEGIN
--This top line essentially does a "SELECT *" from the named table
--and looks for a match based on the "ON" statement below
MERGE INTO glob1app.GFS_TS_JOBDATA_TAB tsj
--This select statement is used for the INSERT when no match
--is found and the UPDATE when a match is found.
--It creates a "pseudo-table"
USING (
SELECT ActivitySeq_in AS ActSeq,
Owner_in As Owner,
NumTrailers_in As NumTrailers,
ReleaseFormReceived_in As ReleaseFormReceived
FROM DUAL) input
--This ON statement is what we're doing the match on to find
--matching records. This decides whether it will be an
--INSERT or an UPDATE
ON (tsj.Activity_seq = ActivitySeq_in)
WHEN MATCHED THEN
--Here we UPDATE based on the passed in input table
UPDATE
SET OWNER = input.owner,
NUMTRAILERS = input.NumTrailers,
RELEASEFORMRECEIVED = input.releaseformreceived
WHEN NOT MATCHED THEN
--Here we INSERT based on the passed in input table
INSERT (
ACTIVITY_SEQ,
OWNER,
NUMTRAILERS,
RELEASEFORMRECEIVED
)
VALUES (
input.actseq,
input.owner,
input.numtrailers,
input.releaseformreceived
);
Response_out := '';
EXCEPTION
WHEN OTHERS THEN
err_num := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 3900);
Response_out := TO_CHAR (err_num) || ': ' || err_msg;
END;
Maby something like
DECLARE V_EXISTS NUMBER;
BEGIN SELECT COUNT(*) INTO V_EXISTS FROM TARGET_TABLE WHERE PK_ID = :ID;
IF V_EXISTS > 0 THEN
-- UPDATE
ELSE
-- INSERT
END IF;
END;
Also, you may try to use so-called tempotary table (select from DUAL)
CREATE TABLE TEST (N NUMBER(2), NAME VARCHAR2(20), ADRESS VARCHAR2(100));
INSERT INTO TEST VALUES(1, 'Name1', 'Adress1');
INSERT INTO TEST VALUES(2, 'Name2', 'Adress2');
INSERT INTO TEST VALUES(3, 'Name3', 'Adress3');
SELECT * FROM TEST;
-- test update
MERGE INTO TEST trg
USING (SELECT 1 AS N, 'NameUpdated' AS NAME,
'AdressUpdated' AS ADRESS FROM Dual ) src
ON ( src.N = trg.N )
WHEN MATCHED THEN
UPDATE
SET trg.NAME = src.NAME,
trg.ADRESS = src.ADRESS
WHEN NOT MATCHED THEN
INSERT VALUES (src.N, src.NAME, src.ADRESS);
SELECT * FROM TEST;
-- test insert
MERGE INTO TEST trg
USING (SELECT 34 AS N, 'NameInserted' AS NAME,
'AdressInserted' AS ADRESS FROM Dual ) src
ON ( src.N = trg.N )
WHEN MATCHED THEN
UPDATE
SET trg.NAME = src.NAME,
trg.ADRESS = src.ADRESS
WHEN NOT MATCHED THEN
INSERT VALUES (src.N, src.NAME, src.ADRESS);
SELECT * FROM TEST;
DROP TABLE TEST;
see here
Its very difficult to tell from you question exactly what you what, but I gather you want the table that you are merging into ( or on ) to be dynamic. In that case, what you should be using is the DBMS_SQL package to create dynamic SQL

Resources