Teradata stored procedure with dynamic parameters called from R script - sql

I need to extract some data from Teradata to process in R. I have around 84 Dep/sec keys with most of them having a different time span so my thought was to create a stored procedure in Teradata that will accept the Dep, Sec and Dates as parameters. I could then loop over the list in R calling the SP each time to create my data set.
The SP I have created to test this idea is a very simple one but I can't get it to work.
CREATE PROCEDURE procTest4 (IntN integer)
BEGIN
CALL DBC.SysExecSQL('SELECT top' || IntN || '*
from TableName');
END;
Teradata does create the SP but I don't know how to execute it and pass the paramters to it. When I try:
Call procText4(10)
I get the following error:
5568: SQL statement is not supported within a stored procedure.
The only other option for me is to create the SQL string in R and then run it from there but there is multiple passes of SQL which create volatile tables and the RODBC package doesn't seem to like them, plus it's a very messy way of doing it.
Any help is much appreciated.

The syntax for returning a result set from a Stored Procedure using Dynamic SQL is a bit complex:
CREATE PROCEDURE procTest4 (IntN INTEGER)
DYNAMIC RESULT SETS 1
BEGIN
DECLARE SqlStr VARCHAR(1000);
DECLARE rslt CURSOR WITH RETURN ONLY FOR stmt;
SET SQLStr = 'SELECT top ' || IntN || ' * from TableName';
PREPARE stmt FROM SqlStr;
OPEN rslt;
END;
But you should double check if you can rewrite those loops...

Related

Debugging BigQuery Stored procedure

Is there any way I can use print statements within BigQuery stored procedure? I have a stored procedure like below, I like to see how SQL statement is generated to debug the issue or any other better way to debug what stored procedure is producing etc.
CREATE OR REPLACE PROCEDURE `myproject.TEST.check_duplicated_prc`(project_name STRING, data_set_name STRING, table_name STRING, date_id DATE)
BEGIN
DECLARE sql STRING;
set sql ='Select date,col1,col2,col3,count(1) from `'||project_name||'.'||data_set_name||'.'||table_name|| '` where date='||date_id ||' GROUP BY date,col1,col2,col3 HAVING COUNT(*)>1';
--EXECUTE IMMEDIATE (sql);
print(sql)
END;
There are number of approaches for debugging / troubleshooting stored proc
One of the simplest - to see how SQL statement is generated - slightly adjust your stored proc as in below example
CREATE OR REPLACE PROCEDURE `myproject.TEST.check_duplicated_prc`(project_name STRING, data_set_name STRING, table_name STRING, date_id DATE, OUT sql STRING)
BEGIN
-- DECLARE sql STRING;
set sql ='Select date,col1,col2,col3,count(1) from `'||project_name||'.'||data_set_name||'.'||table_name|| '` where date='||date_id ||' GROUP BY date,col1,col2,col3 HAVING COUNT(*)>1';
--EXECUTE IMMEDIATE (sql);
END;
Then, you can run below to see generated SQL
DECLARE sql STRING;
CALL `myproject.TEST.check_duplicated_prc`('project_name', 'data_set_name', 'table_name', '2020-11-24', sql);
SELECT sql;
with output
As you can see here - you are missing apostrophes here where date=2020-11-24 so you can fix your stored proc

How to execute dynamic SQL statement inside Postgres sql script

Below is my script. I used Execute command to run the dynamic sql statement that i created but it doesn't run and give an output. It doesn't give an error either. How can I run this sql statement inside postgres sql script?
DO $$
DECLARE sqlCommand varchar(1000);
columnList varchar(75);
BEGIN
select '"BRANCH_NAME", "ADDRESS1"' into columnList;
select concat('SELECT ' , columnList , ' FROM "TESTSCHEMA"."BRANCH" ') into sqlCommand;
raise info 'COMMAND %', sqlCommand;
execute sqlCommand;
END $$;
Your example is safe, but if you are dealing with user input, always use the format function to construct the SQL string.
Anyway, your problem is that the EXECUTE statement has no INTO clause with a destination for the query results, si they are simply discarded.

Using Oracle bind variables in an application which only supports standard SQL

I have an application which works fine to execute and parse standard SQL queries, but not PL/SQL.
I'm trying to use bind variables, and because of lack of PL/SQL support I thought I could create a function that accepts a single variable, queries a DB, and returns the result.
My function is as follows:
CREATE OR REPLACE FUNCTION BIND_FUNC
(
IN_FLOAT IN BINDING_TEST.C2_FLOAT%TYPE
) RETURN SYS_REFCURSOR AS
BINDTEST_CURSOR SYS_REFCURSOR;
BEGIN
OPEN BINDTEST_CURSOR FOR
'SELECT * FROM BINDING_TEST WHERE C2_FLOAT = :IN_FLOAT' USING IN_FLOAT;
RETURN BINDTEST_CURSOR;
END BIND_FUNC;
My problem is that I'm not sure how to make the return value appear to my application as a normal result set.
In SQL Developer if I run the query SELECT BINDING_FUNC(1) FROM BINDING_TEST I get a result like:
{<C1_VARCHAR2=This is some text,C2_FLOAT=1>,}
If I do something like below I get more along the results I'm looking for, but again the syntax doesn't appear supported in my application:
VARIABLE cur REFCURSOR;
BEGIN
:cur := BIND_FUNC(1);
END;
/
PRINT cur;

Oracle Native Dynamic SQL PL/SQL statement without begin and end

I have the problem of creating a backup for a table by creating a series of insert statements.
The input is the table name and each table can have a different number of columns. It is assumed that data types can be varchar2, number, or date only
so I have this line of code:
execute immediate fetchStmt;
where fetchStmt can be:
fetch tableColCursor into valuesArray(1), valuesArray(2), ..., valuesArray(n)
This just fetches each row from the cursor and puts it into a varray, the statements itself works if it is not in an execute immediate statement.
I do know that an execute immediate can only process SQL queries or PL/SQL blocks.
The problems is how would I be able to make this work or what can be a similar solution to the problem?
Note that it is not known during compile time the table and its columns and their data types
EXECUTE IMMEDIATE can only process full statements, i-e: SQL statements or PLSQL blocks (with [DECLARE]..BEGIN..END).
Furthermore, a block executed this way won't see any variables from the calling block (they don't share the same scope), for instance this won't work:
DECLARE
l NUMBER := 1;
k NUMBER := 0;
BEGIN
EXECUTE IMMEDIATE 'BEGIN l := k; END;';
END;
The above code will produce an error because l and k are not defined in the sub-block. Instead you would need to use input/output variables:
DECLARE
l NUMBER := 1;
k NUMBER := 0;
BEGIN
EXECUTE IMMEDIATE 'BEGIN :P1 := :P2; END;' USING OUT l, k;
dbms_output.put_line(l); -- return 0
END;
In your case you don't know the number of variables so you won't be able to use EXECUTE IMMEDIATE. You could use DBMS_SQL but I think there are simpler methods: you could find already working code (it exists within Oracle APEX for instance), or you could program it yourself by running a SELECT that would produce the necessary INSERT string. For this method you would have to generate the SQL statement dynamically (dependent upon the table columns). The statement would look like this (for a table TEST(a,b,c) where all columns are integer [needs adapting for other datatypes]):
SELECT 'INSERT INTO test(a,b,c) VALUES ('||a||', '||b||', '||c||');'
FROM test

Oracle Stored Procedure with Alter command

I am trying to build an Oracle stored procedure which will accept a table name as a parameter. The procedure will then rebuild all indexes on the table.
My problem is I get an error while using the ALTER command from a stored procedure, as if PLSQL does not allow that command.
Use the execute immediate statement to execute DDL inside PL/SQL.
create procedure RebuildIndex(index_name varchar2) as
begin
execute immediate 'alter index ' || index_name || ' rebuild';
end;
I tested this code; it works.
Documentation.
Passing Schema Object Names As Parameters
Suppose you need a procedure that
accepts the name of any database
table, then drops that table from your
schema. You must build a string with a
statement that includes the object
names, then use EXECUTE IMMEDIATE to
execute the statement:
CREATE TABLE employees_temp AS SELECT last_name FROM employees;
CREATE PROCEDURE drop_table (table_name IN VARCHAR2) AS
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE ' || table_name;
END;
/
Use concatenation to build the string,
rather than trying to pass the table
name as a bind variable through the
USING clause.
In addition, if you need to call a
procedure whose name is unknown until
runtime, you can pass a parameter
identifying the procedure. For
example, the following procedure can
call another procedure (drop_table) by
specifying the procedure name when
executed.
CREATE PROCEDURE run_proc (proc_name IN VARCHAR2, table_name IN VARCHAR2) ASBEGIN
EXECUTE IMMEDIATE 'CALL "' || proc_name || '" ( :proc_name )' using table_name;
END;
/
If you want to drop a table with the
drop_table procedure, you can run the
procedure as follows. Note that the
procedure name is capitalized.
CREATE TABLE employees_temp AS SELECT last_name FROM employees;
BEGIN
run_proc('DROP_TABLE', 'employees_temp');
END;
/
Here are a couple of possibilities. First, you would have to treat the SQL as dynamic SQL. Second, Oracle DDL statements cannot be run in a transaction (or, they terminate the current transaction and cannot themselves be rolled back). This may affect whether you can use them in stored procedures, or where you can use stored procedures that contain them.
If none of the above apply at all - there could easily be something else astray - I suggest posting some code.