Unable to retrieve multiple rows using cursor from database table using stored procedure DB2 - stored-procedures

I am trying to retrieve multiple rows from table using stored procedure and it doesn't work for me. My stored procedure is as follows,
CREATE OR REPLACE PROCEDURE E_Enquiry
(IN SourceQueue1 VARCHAR(30) ) LANGUAGE SQL
BEGIN
DECLARE C1 CURSOR FOR
select CreationTime
from ms.Exception_Message
where SourceQueue = SourceQueue1;
open c1;
END;
I am trying to call the stored procedure from Mule Anypoint Studio using the Database connector and have seen the result as null when I print the payload with a logger. If I try to modify the query as it returns a single row it works (without using cursor).
Please help to resolve this. Thanks.

What platform and version of DB2?
Try the adding
DYNAMIC RESULT SETS 1
WITH RETURN TO CLIENT
Like so:
CREATE OR REPLACE PROCEDURE E_Enquiry
(IN SourceQueue1 VARCHAR(30) )
LANGUAGE SQL
DYNAMIC RESULT SETS 1
BEGIN
DECLARE C1 CURSOR WITH RETURN TO CLIENT FOR
select CreationTime
from ms.Exception_Message
where SourceQueue = SourceQueue1;
open c1;
END;

Related

Monitoring a table attribute with stored procdeure

I am working on a stored procedure that monitors the Last_Extract_Ts value in the table that provides information about Extract Transform Load(ETL). Now I want to check whether the Last_Extract_ts value changed from the last time the procedure ran, but I can't quite figure out how to store the result of the last procedure run so that I can use it in the current one.
Below is my procedure
create or replace PROCEDURE MONITOR AS
v_count number:=0;
v_Last_Extract_Ts VARCHAR2(80) := '';
v_Last_ETL_Run VARCHAR2(80) := '';
BEGIN
select count(*) into v_count from oms_etl_config where ATTR_NM='last_extract_ts' and process_type='upd' and ATTR_VALUE<=to_char(sys_extract_utc(systimestamp)-5/1440,'YYYY-MM-DD HH24:MI:SS');
select Attr_value into v_Last_Extract_Ts from OMS_ETL_CONFIG where PROCESS_TYPE='upd' AND ATTR_NM='last_extract_ts';
Select MAX(START_TS) into v_Last_ETL_Run from OMS_ETL_AUDIT;
dbms_output.put_line(v_count);
dbms_output.put_line(v_Last_Extract_Ts);
dbms_output.put_line(v_Last_ETL_Run);
END;
I came across something like storing the result of the stored procedure in a temp table in Insert results of a stored procedure into a temporary table , Exec stored procedure into dynamic temp table but I can't quite see how it meets my needs.
Is what I am trying to achieve possible or I need to have a different approach.
Thanks in advance.
P.S. I am absolute beginner with PL/SQL and stored procedures so I am
not having any attempt in my post to show for the research I have done. Sorry for that.
The simplest way is to save the last results in a table.
create a table:
Create table monitor_results
(
last_run_date date
, last_Last_Extract_Ts varchar2(80)
, last_ETL_Run varchar2(80)
, last_count NUMBER
);
initialize values:
insert into monitor_results values (NULL, NULL, NULL, NULL);
commit;
in the stored procedure update the values in the table:
...
update monitor_results
set
last_run_date = SYSDATE
, last_Last_Extract_Ts = v_Last_Extract_Ts
, last_ETL_Run = v_Last_ETL_Run
, last_count = v_count
;
commit;
You can do this check using a trigger: See below:
CREATE OR REPLACE TRIGGER reporting_trigger
AFTER UPDATE ON <Table>
FOR EACH ROW
BEGIN
/**Your column which holds the record**/
IF :new.a = :old.a THEN
raise_application_error( -20001, 'This is a custom error' );
END IF;
END;

How to get the AUTOINC value when connecting via ADO?

I am trying to connect my database (Advantage 7.1 Server) using the Advantage OLE DB provider. So far so good...It connects with no problem with the code below:-
const
// the database we'll be connecting to
ConnectionString = 'Provider=Advantage OLE DB Provider;Data Source=C:\Data\'+
'UsersData.add;ServerType=ADS_REMOTE_SERVER|ADS_LOCAL_SERVER;User ID=ISUsers;Password=aAoO31';
My problem is, even though I'm able to connect to the database, any field with AUTOINC as the data type does not generate next numbers. "ID" as AUTOINC keeps on giving me zero (0) anytime I append the data instead of moving to the next number 1, 2, 3.... But for the same code if I switch to MS ACCESS, it works perfectly. What am I doing wrong? please find code below.
// Add template to database. Returns added template ID.
function TDBClass.addTemplate(template: TTemplate): Integer;
var
rs: TADODataSet;
tptStream: TMemoryStream;
id: Integer;
p: PChar;
begin
// get DB data and append one row
rs := TADODataSet.Create(nil);
rs.Connection := connection;
rs.CursorType := ctStatic;
rs.LockType := ltOptimistic;
rs.CommandText := 'SELECT * FROM enroll';
rs.Open();
rs.Append();
tptStream := TMemoryStream.Create();
// write template data to memory stream.
SafeArrayAccessData(template.tpt, Pointer(p));
tptStream.write(p^, template.size);
SafeArrayUnaccessData(template.tpt);
// save template data from memory stream to database.
(rs.FieldByName('template') as TBlobField).LoadFromStream(tptStream);
// update the database with added template.
rs.post();
// get the ID of enrolled template.
id := rs.FieldByName('ID').AsInteger;
// close connection
tptStream.Free();
rs.Close();
rs.Free();
addTemplate := id;
end;
You should consider using the TADSConnection, TADSQuery, etc. components that you can still download from the DevZone (http://devzone.advantagedatabase.com/dz/content.aspx?key=1) even for ADS 7.1.
If you have to use ADO, you probably have to use a different approach. (But see also bummi's comment about a possible bug in Delphi).
One way would be to use the LASTAUTOINC scalar function:
INSERT INTO
enroll
(
template
)
VALUES
(
:template
);
SELECT
LASTAUTOINC(CONNECTION) AS "id"
FROM
system.iota
In Delphi ADO you can pass more than 1 statement in your SQL query string - This might true for ADO in general.
Insert into test (text) Values('ddd'); Select * from test where AutoIncColumn = Scope_identity()
This will return the inserted row with the Autoinc value
or if you need just the autoinc value
Insert into test (text) Values('ddd'); Select Scope_identity() AutoIncColumn

Calling an oracle stored procedure form COGNOS

In this link IBM they explain how to use an Oracle stored procedure with COGNOS.
They are doing :
create or replace package body project_pk as
procedure project_sp (tproj IN numeric, result1 in out project_type1) is
begin
open result1 for
select projects.projectid, projects.projecttype
from projects
where projects.projecttype=tproj;
end;
end;
/
With open result1 for they are opening the cursor explicitly. But, they do not seem to close it. Why is this?
You must return resuly set to Cognos. If you close the cursor then there are nor reuslts, right?
It's Cognos responsibility to close the cursor, once it finishes to pull the data from the SP.
In order to make you 100% sure that this is the case look at this link (totaly unrelated to Cognos):
Returning result Sets from SQL Server and Oracle
However, the sample you gave in your link, looks quite complex. Here is what I am using:
CREATE OR REPLACE PROCEDURE "COGNOS_SP" (
case_id in numeric,
po_refcur out sys_refcursor) is
BEGIN
open po_refcur for
select * FROM CASES WHERE CASE_ID = case_id;
END COGNOS_SP;

Firebird TIBQuery insert with returning ... INTO

I have a firebird 2.x database with Generator and a trigger to generate the key field.
I need to get the returned value from below query.
INSERT INTO XXXX (vdate,description) values ('"+ VDate +"','"+ Description +"') returning vno INTO :ParamVoucherNo
I tried several versions of below code but it dont wrok and I get
Dynamic sql error sql error code = -104
Is it really possible to get the return value in delphi using TIBQuery ?
Query1->SQL->Clear();
Query1->SQL->Add("INSERT INTO XXXX (vodate,description) values ('"+ VDate +"','"+ Description +"') returning vno INTO :ParamVoucherNo");
Query1->Params->ParamByName("ParamVoucherno")->ParamType = ptResult;
Query1->Params->ParamByName("ParamVoucherno")->DataType = ftInteger;
Query1->Params->ParamByName("ParamVoucherno")->Value = "";
Query1->Prepare();
Query1->ExecSQL();
Any suggestions?
From Firebird README.returning:
The INTO part (i.e. the variable list) is allowed in PSQL only (to
assign local variables) and rejected in DSQL.
As IBX uses DSQL, you should exclude INTO part from your query.
INSERT ... RETURNING for DSQL looks the same as a call of a stored procedure, which returns result set. So, you have to use Open instead of ExecSQL.
Your mixing of dynamic SQL with parameters is just confusing.
Do this instead:
Query1->SQL->Clear();
Query1->SQL->Add("INSERT INTO table1 (vodate,description) VALUES"+
"(:VoDate,:Description) RETURNING vno INTO :VoucherNo ");
Query1->Params->ParamByName("VoDate")->Value = VDate;
Query1->Params->ParamByName("description")->Value = Description;
Query1->Prepare();
Query1->ExecSQL();
VoucherNo = Query1->Params->ParamByName("VoucherNo")->AsInteger;
Using Delphi 6 I have the ID returning successfully using an EXECUTE BLOCK statement:
EXECUTE BLOCK
RETURNS ( DeptKey INT )
AS
BEGIN
INSERT INTO DEPARTMENT
( COMPANY_KEY, DEPARTMENT_NAME )
VALUES ( 1, 'TEST1' ) RETURNING DEPARTMENT_KEY INTO :DeptKey;
SUSPEND;
END;
From Delphi you can do the folliowing:
FQuery.SQL.Text := '<Execute Block Statement>';
FQuery.Open();
ANewKey := FQuery.Fields[0].AsInteger;
IBX is not Firebird ready
you can take a look at FIBPLUS who support Firebird features
FIBPlus also supports FB2.0 insert ... into ... returning. Now you
should not bother about getting generator values from the client but
leave them in the trigger. You can also use RDB$DB_KEY. New possible
variants of work with insert returning and RDB$DB_KEY are shown in the
example “FB2InsertReturning”.
Why not get the next value for VoucherNo first, followed by
"INSERT INTO table1 (vno, vodate,description) VALUES (:VoucherNo,:VoDate,:Description)");
?
Your trigger can then either be dispensed with (which is nice), or modified to detect null (or <= zero can be useful too) and only then populate the vno field.
create trigger bi_mytable
active before insert position 1
on mytable
as
begin
if (new.vno is null)
then new.vno = next value for gen_VoucherNos;
end
Client-side you can :
select gen_id(gen_VoucherNos, 1) from rdb$database;
By modifying the trigger in this manner you save yourself a headache later on if/when you want to insert blocks of records
I wonder if that INSERT can be wrapped into EXECUTE BLOCK command.
Would IBX manage EXECUTE BLOCK then?
http://www.firebirdsql.org/refdocs/langrefupd20-execblock.html
http://firebirdsql.su/doku.php?id=execute_block
Hope to try it in both IBX and Unified Interbase in XE2
PS: Even if it does not, I found the library, that tells to work on top of IBX of Delphi XE2 (both x86 and x64) and to add EXECUTE BLOCK support: http://www.loginovprojects.ru/index.php?page=ibxfbutils#eb.
As I know there should be some changes to IBX made. Internally INSERT ... RETURNING should be treated the same way as a selectable procedure with returning parameters.
i know this question was answered a long time ago, but i must write this as clear as possible, for those who need this as i was.
i too, needed the "INSERT..RETURNING" thing.
the Delphi drove me crazy for a long time, until i changed my Data access components.
i even moved from Delphi XE2, to XE5 only because of that...
conclusion : IBX does NOT support RETURNING!
FireDAC is PERFECT for what i need with Firebird.
just move to FireDAC and you'll be able to do everything you need, and with high performance.
If you have a table with this 2 Fields: GRP_NO and GROUPNAME and you want to get the new GRP_NO you have to use RET_ as prefix, see example:
procedure TFormDatenbank.Button1Click(Sender: TObject);
var
q: Uni.TUniQuery;
ID: Integer;
GroupName: String;
begin
GroupName := 'MyGroupName';
q := TUniQuery.Create(nil);
try
q.Connection := Datenmodul.UniConnection;
q.ParamCheck := true; // the default value of ParamCheck is true.
q.SQL.Clear;
q.SQL.Add('SELECT GRP_NO, GROUPNAME FROM GROUPDATA WHERE GROUPNAME = :GROUPNAME');
q.ParamByName('GROUPNAME').AsString := GroupName;
q.Open;
if q.RecordCount > 0 then
ID := q.FieldByName('GRP_NO').AsInteger
else
begin
// there exist no group with this name, so insert this new name
q.SQL.Clear;
q.SQL.Add('INSERT INTO GROUPDATA');
q.SQL.Add('(GROUPNAME)');
q.SQL.Add('VALUES');
q.SQL.Add('(:GROUPNAME)');
q.SQL.Add('RETURNING GRP_NO;');
q.ParamByName('GROUPNAME').AsString := GroupName;
q.Execute;
ID := q.ParamByName('RET_GRP_NO').AsInteger;
end;
finally
q.Free;
end;
end;
From the IBx2 sources, you can do it like this:
//Uses IBSql;
//var Cur: IResults;
IBSQL1.SQL.Text := 'delete from tbl_document where id = 120 returning id;';
IBSQL1.Prepare;
if IBSQL1.Prepared then
begin
Cur := IBSQL1.Statement.Execute(IBTransaction1.TransactionIntf);
WriteLn(Cur.Data[cou].AsString);
Cur.GetTransaction.Commit(True);
end;
IResults interface Code:
IResults = interface
function getCount: integer;
function GetTransaction: ITransaction;
function ByName(Idx: String): ISQLData;
function getSQLData(index: integer): ISQLData;
procedure GetData(index: integer; var IsNull:boolean; var len: short; var data: PChar);
procedure SetRetainInterfaces(aValue: boolean);
property Data[index: integer]: ISQLData read getSQLData; default;
property Count: integer read getCount;
end;
Test enviroment:
Arch Linux X86
Firebird 3
Lazarus 1.9
FPC 3.0.4
Quick note: This works on new Firebird API in the IBX, But I didn't test it in Legacy Firebird API with the IBX.

EXECUTE IMMEDIATE with USING clause giving errors

All,
I am very new to stored procedures in general but I am struggling especially with those in Oracle. I have created a very simple example of what I am trying to accomplish and I am still getting the same error with this simplified version.
The example stored procedure is as follows:
CREATE OR REPLACE PROCEDURE ashish_test
AUTHID CURRENT_USER IS
BEGIN
DECLARE
v_tab VARCHAR2(50);
v_strSQL VARCHAR2(50);
BEGIN
v_strSQL := 'SELECT * FROM :1';
v_tab := 'ex.emp';
EXECUTE IMMEDIATE v_strSQL USING v_tab;
END;
END;
When I call the above stored procedure using CALL ashish_test(), I get :
Error Message http://web1.twitpic.com/img/12831839-06a3ea536df5d5a0a839eb83d9e59d25.4a3936b8-scaled.jpg
Based on this article (Look for Example 7-1), USING keyword should replace the numbered placeholder (:1) within v_strSQL with the value stored in v_tab. However, I keep getting invalid table error. I am guessing it's because EXECUTE IMMEDIATE is unable to replace the placeholder with the value for some reason but I am not sure why that is. Does anyone know if I am doing something stupid here?
I am running this on Oracle 10g database & using PL/SQL Developer.
The USING clause is only for bind variables (i.e. where you would use column names in a select statement), not table names. Typical usage would look like this:
Select col1 from table1 where col2 = :a
If you want to use variable table names use something like this:
v_tab := 'ex.emp';
v_strSQL := 'SELECT * FROM ' || v_tab;
EXECUTE IMMEDIATE v_strSQL;

Resources