Variable number of arguments in PL/SQL stored procedure - stored-procedures

Can a procedure PL/SQL take a variable number of arguments?
In my case, the procedure is called by the submit button of a form, and the form has variable number of inputs.

You don't mention it, but are you using mod_plsql?
If so, you should read about flexible parameter passing.
In short, prefix your procedure name with an exclamation mark in your browser and define your procedure with a name_array and value_array.

Sort of. You can give the procedure parameter default values:
CREATE PROCEDURE myproc( p_value_a NUMBER DEFAULT 1,
p_value_b NUMBER DEFAULT 2 ) AS
...
which you could call like this:
myproc( 999 );
or like this:
myproc (p_value_b => 11 );

Related

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.

Pass NULL parameter to TFDConnection.ExecSQLScalar

Is there a possibility to pass NULL value to some parameter of this FireDAC query?:
conn: TFDConnection;
fPar1, fPar2, fPar3: OleVariant;
cnt := conn.ExecSQLScalar(
'SELECT COUNT(*) FROM my_table WHERE par1=:p1 AND par2=:p2 AND par3=:p3',
[fPar1, fPar2, fPar3]
);
Is it possible without intermediate TFDQuery using TFDConnection object only?
Yes, you can do this, despite the fact that the parameters TFDConnection uses
for ExecSQLScalar are not directly accessible from your calling code, but it may not produce the result you are expecting unless you modify your SQL - see below.
Presumably, you have had an error message like "[FireDAC] parameter type [fPar2 ] is unknown ..." if you set fPar2 to Null beforehand.
You can avoid that by using the override of ExecSQLScalar that allows you to specify
the field types of the parameters in an open array following the parameter
which lists the variants, as in e.g.
cnt := conn.ExecSQLScalar(
'SELECT COUNT(*) FROM my_table WHERE par1=:p1 AND par2=:p2 AND par3=:p3',
[fPar1, fPar2, fPar3],
[ftString, ftString, ftString] // or whatever
);
See
function TFDCustomConnection.ExecSQLScalar(const ASQL: String;
const AParams: array of Variant; const ATypes: array of TFieldType): Variant;
in FireDAC.Comp.Client.Pas
BUT, on my data here, this does NOT produce the correct count value (using Seattle and SS2014) presumably because of Uwe Raabe's good point about par1 = Null versus par1 is Null. To get the correct answer, I had to modify the SQL as per Keith Miller's comment to include set ansi_nulls off before SELECT ...

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

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

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.

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