How to define an Array of values (or a Column) into a Procedure Argument? - stored-procedures

I am working on a Netezza SP and is stuck with a problem.
I have a SP, defined as say:
CREATE OR REPLACE PROCEDURE MY_PROC(VARCHAR(ANY)) RETURNS INTEGER LANGUAGE NZPLSQL
AS
BEGIN_PROC
DECLARE
v_temp ALIAS FOR $1;
/* Other decalarations */
result_ts INTEGER;
BEGIN
result_ts := 0;
/* Procedure Body */
RETURN result_ts;
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'Exception Raised: %', SQLERRM;
END;
END_PROC;
If I am running this SP with one value, such as:
SELECT MY_PROC('TEST_INPUT');
But if I am trying to run it with a column value, such as:
SELECT MY_PROC(TEST_COLUMN) FROM TEST_TABLE;
Its giving me error as:
ERROR: Can't use a stored procedure in this context
I know that in the second scenario I am passing an Array (i guess) but this is not what the Procedure has expected.
Now I am trying to have a procedure that can accept these kind of values but could not succeeded so far, LOOPing and all I have taken care but only problem is the Argument which I don't know how to pass.
Any help would be appreciated, let me know if I need to provide any extra info on this.
Asif

Stored procedures in Netezza, as of v7.2, can only be called in the following ways, as documented here.
CALL sproc_name(...);
EXEC sproc_name(...);
SELECT sproc_name(...);
Note that the SELECT form does not allow a FROM clause.
If you want the stored procedure to act on a particular column from a particular table that changes from invocation to invocation, you could pass the names of those as arguments to the stored procedure and have the entirety of the SQL logic encoded within. You could even pass arbitrary code into the stored procedure to build a query internally.
The way you are trying to call it now is more like calling a user defined function, and that simply won't work with stored procedures here.

Related

Accept user input and set assign to variable PL/SQL

I am trying to accept user input inside of a stored procedure and assign it to a VARCHAR variable. For some reason I get error
PLS-00201: identifier 'userinput' must be declared.
Any ideas? I have to use this later down the line to see how many times the input appears in a table.
CREATE OR REPLACE PROCEDURE nums
AS
x_num VARCHAR(20);
BEGIN
x_num := &input;
dbms_output.put_line('You entered: ' || x_num);
END;
/
Procedures cannot receive user input in response to a prompt, PLSQL is NOT interactive. When you have that you are not actually communicating with the database. What is actually happening is symbol substitution were SQLPLUS or other interface (Toad , SQL Developer, ...) is actually intercepting symbol, requesting the input, and physically changing the script before submitting it to the database. If you want a stored procedure you will need to use a parameter as #HereGoes suggested and then provide the user a script as follows:
Begin
nums(pInput => &Input);
end ;
Or provide an application interface to receive the input value and call the procedure or allow user access through SQLPLUS or other interface and let them enter the script - not recommended.
I suggest making the input a parameter.
CREATE OR REPLACE PROCEDURE nums (pInput IN VARCHAR2)
AS
x_num VARCHAR(20);
BEGIN
x_num := pInput ;
dbms_output.put_line('You entered: ' || x_num);
END;
/

Display result of a variable in teradata stored procedure

I need to print results of variables in Teradata stored procedures, but print does not work in teradata, should I use the below method or please suggest the proper one:
My code:
REPLACE PROCEDURE Name()
---some code goes here
BEGIN
TRANSACTION;
SET var1= var2+ 3;
SET var3= var2;
/* !!! PRINT var1*/
/* !!! PRINT var3*/
end;
--some code here
I used the out in stored procedure declaration, suggest me another way to print the values of variable:
REPLACE PROCEDURE Name(out var1, out var3)
As long as there's only a single line of values has to be returned you can utilize OUT variables.
For multiple lines you can use a Volatile Table where those lines are inserted and a final DYNAMIC RESULT SET returns them as an answer set.
If you want to print the value of a variable inside a stored procedure I assume you are asking in the context of debugging the store procedure. Teradata doesn't appear to have the debugging tricks that other databases provide, e.g. RAISE and NOTICE. One workaround is the create a log table with at least one column, message VARCHAR(500) and then insert your debugging messages into that table.

passing arrays as parameters to plsql procedure

I need was playing around with sql developer and I have ran into a wall here...
I need to run a simple update query on a table and I want to pass in an array of Ids and update all the rows pointed by those Ids.
I have written the following stored procedure
PROCEDURE SAMPLE_PROCEDURE(SAMPLE_ARRAY IN NUM_ARRAY)
AS
BEGIN
UPDATE RETURNLIST_PICKLIST_MAPPING
SET PICKLIST_ID = 1111111
WHERE RETURNLIST_ID IN (SELECT * FROM TABLE(SAMPLE_ARRAY));
END SAMPLE_PROCEDURE;
NUM_ARRAY is a custom type defined as follows
create or replace
TYPE NUM_ARRAY
AS VARRAY(40) OF NUMBER(38, 0);
When I run the stored procedure in sql developer I want to input the value for SAMPLE_ARRAY. I have tried (2222,1111,1234) and [2222,1111,1234] and {2222,1111,1234} and each time I get "expression is of wrong type" error.
I desperate need help with this guys....
You did not show how did you assign values to your varray variable. But, I believe you can do it like this:
DECLARE
V_T NUM_ARRAY;
BEGIN
V_T := NUM_ARRAY(1,2,3);
SAMPLE_PROCEDURE(V_T);
END;
/
In general, you can define a standalone VARRAY as follows:
CREATE Or REPLACE TYPE varray_type_name AS VARRAY(n) OF <element_type>;
Or, within PL/SQL block:
TYPE varray_type_name IS VARRAY(n) of <element_type>
Refer to this for more details

DB2 SQL procedure - Calling initiate procedure before declaration

I am making an sql procedure (in lib EXTLIB) that will be called from an external system using JDBC. We have a user, EXTUSER, that is used for running all requests coming from there. The problem is that this user can only have a single starting library, which in our case is EXTLIB (if anyone knows how to add more, please do tell me and become a hero in my company). We have a procedure initiate that initiates the environment (i.e. adds all necessary libs to LIBL). This needs to be called at the start of all procedures in EXTLIB.
My procedure needs to fetch data from a view in our internal lib: INTERLIB. The problem is that we are using ARCAD for version/release/everything management, and ARCAD doesn't like qualifiers, so it automatically removes them, meaning that the declaration at the start can't find the view in INTERLIB, and thus it stops before reaching the initate call.
I need a way to run the call before the declaration, while retaining the functionality. I'm also open to any other alternative solutions. The only one I can think of is instead coding it in RPG, but we have a lot of simple procedures for which that would just be a waste of time. manually creating the procedure in STRSQL is not an option, because when ARCAD transfers a version to production it will recompile everything, thus removing any manual qualifiers we have put there.
Here's the code:
CREATE PROCEDURE MyProcedure ()
LANGUAGE SQL
SPECIFIC MYFILE
CALLED ON NULL INPUT
DYNAMIC RESULT SETS 1
BEGIN
declare c1 cursor with return to client for
select
id,
required
from INTERLIB.cus_CustomerPurpose_v3_0
call INITIATE();
open c1;
call DEINITIATE();
END;
I think you have two options:
1) To create an internal block in the stored procedure,
CREATE PROCEDURE MyProcedure ()
LANGUAGE SQL
SPECIFIC MYFILE
CALLED ON NULL INPUT
DYNAMIC RESULT SETS 1
BEGIN
call INITIATE();
BEGIN
declare c1 cursor with return to client for
select
id,
required
from INTERLIB.cus_CustomerPurpose_v3_0;
open c1;
END;
call DEINITIATE();
END #
2) To call another stored procedure
CREATE PROCEDURE MyProcedure ()
LANGUAGE SQL
SPECIFIC MYFILE
CALLED ON NULL INPUT
DYNAMIC RESULT SETS 1
BEGIN
call INITIATE();
call another_SP();
call DEINITIATE();
END #

Assign an existing method by its name from one Unit to a generic TMethod in another unit

Basically, I have an application which will retrieve information out of an INI file and part of this process includes extracting the names -stored in the INI- of some procedures declared at global scope inside another unit.
I use the following to get the SQL method:
MyIni.ReadString('SQL', SubStr + '_Insert, '');
SubStr is the type of data I want, for example "titles", this is prefixed with the type of SQL procedure I want, in this case "_Insert" therefore, my request here could be seen as:
MyIni.ReadString('SQL', 'titles_Insert', '');
This would then retrieve the appropriate SQL procedure name "InsertTitlesSql" which is displayed inside the INI thus:
[SQL]
titles_Insert=InsertTitlesSql
I have a unit which lists the SQL procedures we use.
Like so:
unit uSqlLibrary;
interface
function InsertTitlesSql: string;
implementation
function InsertTitlesSql: string;
begin
{
INSERT INTO TITLES (ENGLISH, AFRIKAANS, KEY)
VALUES (:english,
:afrikaans,
:key)
}
Result := ''
+'INSERT INTO TITLES (ENGLISH, AFRIKAANS, KEY) '
+'VALUES (:english, '
+' :afrikaans, '
+' :key) ';
end;
end.
I've tried TGenericContainer without any success, I've also tried MethodAddress but I don't know how to tell MethodAddress to look at the other unit without an Object to reference (Form1.MethodAddress() for example) my best result so far is this:
type
TExec = procedure of object;
procedure TForm1.Button1Click(Sender: TObject);
var
M : TMethod ;
E : TExec ;
begin
M.Code := #Test; // Where "Test" is a procedure inside a secondary Unit.
E := TExec(M);
E;
end;
What I'm trying to do is get the SQL procedure by name that I want ( function InsertTitlesSql : string;) and assign it to a generic method that behaves in the same way.
My team lead has said that he doesn't want to edit the uSqlLibrary; so I can't do it that way and have thought to go by the method's name instead. Any other suggestions are welcome.
Hope this is clear. Sorry if it's not ( my use of terminology is not so good xD ). I'll try to elaborate on your queries to the best of my knowledge if I can.
You cannot use RTTI to enumerate procedures with global scope. You can enumerate methods, so you could convert your global procedures to be static class methods.
However, you also state that your team lead does not want you to change uSqlLibrary. This seems a little short sighted in my opinion. Feel free to tell him/her that I said so.
Anyway, if you cannot change uSqlLibrary then you cannot use RTTI. So you'll need to construct your own lookup table. Use a generic dictionary:
uses
System.Generics.Collections;
var
ProcTable: TDictionary<string, TProc>;
Instantiate it in the usual way. Add your functions at program startup:
....
ProcTable.Add('InsertTitlesSql', InsertTitlesSql);
....
When you need to look one up and call it do this:
var
Proc: TProc;
....
if not ProcTable.TryGetValue(ProcName, Proc) then
raise EProcNotFound.CreateFmt(...);
Proc();
The default equality comparer used for the key is case-sensitive by default. So you may elect to supply a custom equality comparer that compares keys without case sensitivity.

Resources