How to call a user defined function using the 'CALL' statement? - sql

I was going thru the Oracle documentation for the CALL statement. It is indicated there that the CALL statement can be used to call a user defined function by using INTO (not a function inside a package). I've tried loads of combinations but can't seem to get the right one. Can someone give me an example on how to do this? Thanks.
EDIT:
I've tried the example below in SQL Developer but i am getting an error.
variable x number;
call f(10) into :x;
I'm getting a bang in line 2 and the error:
SQL Error: ORA-01008: not all variables bound<br>
01008. 00000 - "not all variables bound"

From the Oracle documentation:
VARIABLE x VARCHAR2(25);
CALL warehouse_typ(456, 'Warehouse 456', 2236).ret_name()
INTO :x;
Another example:
create function f(n number) return number is
begin
return n * 2;
end;
SQL> variable x number;
SQL> call f(10) into :x;
Call completed.
SQL> print x;
X
----------
20

Related

How to execute function with parameter in PostgreSQL using anonymous code block in FireDAC?

My database (PostgreSQL 11) has a function that must be called to execute an action. The return is not important, as long as there's no error. This function has arguments that must be passed as parameters.
I cannot directly use TFDConnection.ExecSQL because I use parameters of type array that is not supported by the method (to my knowledge). So, I use TFDQuery.ExecSQL like this:
msql1: = 'DO $$ BEGIN PERFORM DoIt (:id,:items); END $$; ';
{... create FDQuery (fq), set connection, set SQL.Text to msql1}
fq.Params.ParamByName ('id'). AsInteger: = 1;
{$ REGION 'items'}
fq.ParamByName ('items'). DataType: = ftArray;
fq.ParamByName ('items'). ArrayType: = atTable; // Must be atTable, not atArray
if length (items)> 0 then
begin
fq.ParamByName ('items'). ArraySize: = length (items);
for it: = 0 to length (items) -1 do
fq.ParamByName ('items'). AsIntegers [it]: = items [it];
end;
fq.ExecSQL;
{$ ENDREGION}
When executing the above code, the error above message raises
"Parameter 'id' not found".
After some research that suggested using fq.Params.ParamByName I was also unsuccessful.
However, if you change the way the function is called to
select DoIt (:id,:items); and obviously replacing the execution with fq.Open works perfectly.
Is it possible to execute a PL / pgSQL block that contains parameters in the function called by this block using TFDConnection / TFDQuery?
PS: I'm using Delphi Rio 10.3.3
When you assign a value to TFDQuery.SQL, FireDAC does some pre-processing of the SQL based on various options. ResourceOptions.CreateParams option controls whether parameters should be processed. This is enabled by default.
The preprocessor recognizes string literals in your SQL and doesn't try to look for the parameters in them. You used dollar quoted string constant and that's why FireDAC doesn't recognize parameters in it. Even if you add parameter manually I think that FireDAC would not bind the value.
With that said, the proper way to execute stored procedures/function is to use TFDStoredProc. You just assign StoredProcName and call its Prepare method which will retrieve procedure's metadata (parameters) from database so you don't need to set ArrayType or DataType of the parameter.
In your code you set the DataType to ftArray which is wrong, because in case of array parameter it should be set to array's element type. Anyway, by setting fq.ParamByName ('items').AsIntegers you effectively set parameter's DataType to ftInteger. All you need to do is to set ArraySize
Here's what you should do instead:
procedure DoIt(Connection: TFDConnection; ID: Integer; const Items: TArray<Integer>);
var
StoredProc: TFDStoredProc;
ParamItems: TFDParam;
Index: Integer;
begin
StoredProc := TFDStoredProc.Create(nil);
try
StoredProc.Connection := Connection;
StoredProc.StoredProcName := 'DoIt';
StoredProc.Prepare;
StoredProc.Params.ParamByName('id').AsInteger := ID;
if Length(Items) > 0 then
begin
ParamItems := StoredProc.Params.ParamByName('items');
ParamItems.ArraySize := Length(Items);
for Index := Low(Items) to High(Items) do
ParamItems.AsIntegers[Index] := Items[Index];
end;
StoredProc.ExecProc;
finally
StoredProc.Free;
end;
end;
Alternatively you can use ExecFunc to get the result of stored function.

Oracle function compiles successfully but throws error while executing PLS-00221: is not a procedure or is undefined

I have simple oracle function
create or replace function abs.test_func(test_in in number)
return number
is
test_out number ;
BEGIN
test_out:=test_in;
RETURN test_out;
END;
if I compile it - it compiles successfully.
but when I run from PLSQL Developer SQL Window
BEGIN abs.test_func(5); END;
it I gets the following errors
ORA-06550: line1, column8;
PLS-00221: 'TEST_FUNC' is not a procedure or is undefined
ORA-06550: line1, column8;
PL/SQL: Statement ignored
What's wrong with my function ?
Your create function code looks good, however you are not invoking the function properly. A function returns something, that you must either select, assign, print, or evaluate.
Here are a few examples of valid function calls:
-- print the return value
begin
dbms_output.put_line(test_func(5));
end;
/
1 rows affected
dbms_output:
5
-- select the return value
select test_func(5) from dual;
| TEST_FUNC(5) |
| -----------: |
| 5 |
Demo on DB Fiddle

Issue with dynamic execute immediate query

I have a code in my procedure that looks like this. But when i execute this code, i get the error as mentioned below.
The Error report that i got is:
Error report -
ORA-06553: PLS-306: wrong number or types of arguments in call to 'OGC_Y'
ORA-06512: at line 20
06553. 00000 - "PLS-%s: %s"
*Cause:
*Action:
The error has something to do with primary_flag = "Y" <-- this. How else can i write primary_flag = 'Y' inside a string?
The dynamic query is required in my case.
MY CODE IS:
DECLARE
p_assignee_id NUMBER := 10153;
time_stamp timestamp := '12-DEC-2011';
create_task_view_sql VARCHAR2(4000);
BEGIN
create_task_view_sql:=
'select unique cp.sub_last_name
from cs_sr_contact_points_v cp
where cp.incident_id = 55500
and cp.contact_phone is not null
and primary_flag = "Y"';
dbms_output.put_line(create_task_view_sql);
execute immediate create_task_view_sql;
END;
To embed quoted string in a quoted string, use two single quotes:
'...and primary_flag=''Y''';
Or you can use the newer q' syntax to avoid doubling up the embedded quotes:
q'[...and primary_flag='Y']';

Character string buffer too small error in Oracle Stored Procedure

I am getting an error in an Oracle 11g stored procedure. The error is...
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
It is happening at line 31, the line that contains out_cnt_tot := 0; I'm really not sure why there is anything wrong with that line. Another programmer created this procedure and I'm really not familiar with SQL procedures. Can anyone help me figure this out?
create or replace
PROCEDURE "FIP_BANKREC_PREP"
(
in_file_date in varchar2,
in_bank_code in varchar2,
out_cnt_apx_miss_no out integer,
out_cnt_prx_miss_no out integer,
out_cnt_apx_no_mtch out integer,
out_cnt_prx_no_mtch out integer,
out_cnt_ap_dup out integer,
out_cnt_pr_dup out integer,
out_cnt_bad out integer,
out_cnt_ap_load out integer,
out_cnt_pr_load out integer,
out_cnt_ap_not_load out integer,
out_cnt_pr_not_load out integer,
out_cnt_tot out integer,
out_message out varchar2
) as
file_date date;
ap_acct_no varchar2(16);
pr_acct_no varchar2(16);
-- ------------------------------------------------------
-- begin logic
-- ------------------------------------------------------
begin
file_date := to_date(in_file_date,'yyyymmdd');
out_cnt_tot := 0; --- THE ERROR IS ON THIS LINE ---
out_message := 'Test Message';
select brec_acct_code into ap_acct_no
from MSSU.zwkfi_bankrec_accts
where brec_acct_bank = in_bank_code
and brec_acct_type = 'AP';
select brec_acct_code into pr_acct_no
from MSSU.zwkfi_bankrec_accts
where brec_acct_bank = in_bank_code
and brec_acct_type = 'PR';
// The rest of the procedure...
Simple demo of the scenario mentioned in comments:
create or replace procedure p42(out_message out varchar2) as
begin
out_message := 'Test message';
end p42;
/
If I call that with a variable that is declared big enough, it's fine. I have a 12-char variable, so assigning a 12-char value is not a problem:
declare
msg varchar2(12);
begin
p42(msg);
end;
/
anonymous block completed
But if I make a mistake and make the caller's variable too small I get the error you're seeing:
declare
msg varchar2(10);
begin
p42(msg);
end;
/
Error report:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "STACKOVERFLOW.P42", line 3
ORA-06512: at line 4
06502. 00000 - "PL/SQL: numeric or value error%s"
*Cause:
*Action:
The error stack shows both the line in the procedure that errored (line 3), and the line in the caller that triggered it (line 4). Depending on where you're calling it you might not have the whole stack, of course.
You mentioned that there would be various error messagesin the future. You need to make sure that anything that ever calls this defines the variables to be big enough to cope with any of your messages. If they were stored in a table you could semi-automate that, otherwise it'll be a manual code review check.
OK, saw your c# comment after posting this. It looks like you're calling this constructor; that doesn't say what default size it gets, but not unreasonable to think it might be 1. So you need to call this constructor instead to specify the size explicitly:
OracleParameter(String, OracleType, Int32)
Initializes a new instance of the OracleParameter class that uses the parameter name,
data type, and length.
... something like:
OracleParameter prm15 = new OracleParameter("out_str_message",
OracleDbType.Varchar2, 80);
Unless there's a way to reset the size after creation, which I can't see. (Not something I've ever used!).

Ellucian Banner API

I am new with banner student api. When I run the code below it gives me an error:
ORA-00907: missing right parenthesis
call sb_course.f_query_one(
p_subj_code => "LMA",
p_crse_numb => "400",
p_eff_term => 201203
);
Thanks in advance for you time.
The small code snippet will not produce the error ORA-00907: missing
right parenthesis. That error is either from the context around the
code snippet, or within the code ran by calling f_query_one.
Below, I show
That double quotes around the values in the call do not work. In
Oracle double quotes are a way to specify identifiers that do not
follow the normal identifier rules. The error in this case is
ORA-06576: not a valid function or procedure name
Using single quote works when calling a procedure.
When calling a function, an into clause needs to be specified to
receive the return value. Otherwise the error ORA-06576: not a valid
function or procedure name will be raised.
In no case is ORA-00907: missing right parenthesis raised
Please look at the wider context to find the cause of your error.
The following is the interaction with SQL*Plus. SQL> is a prompt for
new commands, with the lines below with numbers for additional lines
for that one command. Everything else is printed by SQL*Plus.
Make output from the dbms_output package visible.
SQL> set serveroutput on size unlimited
Create a procedure f_query_one taking the augments from your
sample. Types are guessed based on the values passed in your
sample. Note, I do not have an SB_COURSE schema. That will be a
difference from my samples below, and your code snippet.
SQL> create or replace procedure f_query_one(p_subj_code in varchar2
2 , p_crse_numb in varchar2
3 , p_eff_term in varchar2)
4 is begin
5 dbms_output.put_line('Hello World!');
6 end f_query_one;
7 /
Procedure created.
Call as posted in your question, and find that double quotes do not
work.
SQL> call f_query_one(
2 p_subj_code => "LMA",
3 p_crse_numb => "400",
4 p_eff_term => 201203
5 );
p_subj_code => "LMA",
*
ERROR at line 2:
ORA-06576: not a valid function or procedure name
Call with single quotes. Works!
SQL> call f_query_one(p_subj_code => 'LMA'
2 , p_crse_numb => '400'
3 , p_eff_term => 201203);
Hello World!
Call completed.
Drop the procedure and create a function with the name f_query_one.
SQL> drop procedure f_query_one;
Procedure dropped.
SQL> create or replace function f_query_one(p_subj_code in varchar2
2 , p_crse_numb in varchar2
3 , p_eff_term in varchar2) return varchar2
4 is begin
5 return 'Hello World!';
6 end f_query_one;
7 /
Function created.
Call the function. But the function does exist, why does it say it does not?
SQL> call f_query_one(p_subj_code => 'LMA'
2 , p_crse_numb => '400'
3 , p_eff_term => 201203);
call f_query_one(p_subj_code => 'LMA'
*
ERROR at line 1:
ORA-06576: not a valid function or procedure name
Create a bind variable. var is a SQL*Plus command. It is part of
neither the SQL or PL/SQL languages.
SQL> var so varchar2(20)
Add an into clause to save the value in the bind variable.
SQL> call f_query_one(p_subj_code => 'LMA'
2 , p_crse_numb => '400'
3 , p_eff_term => 201203) into :so;
Call completed.
Print the bind variable. print is a SQL*Plus command. It is part of
neither the SQL or PL/SQL languages.
SQL> print so
SO
--------------------------------
Hello World!
Using single quotes should work. Are you sure the error is happening on this line? What you written in your comment looks good.
Strike that.....
You do not want to use the word "call". If you are writing in pl/sql, then drop that word. If you are writing this in sql, then use "exec" instead.