DB2 Stored Procedure using a Cursor - stored-procedures

DB2 V9 Z/Os
CREATE PROCEDURE SERDB.I21MMSNOUPD ()
RESULT SETS 1
LANGUAGE SQL
FENCED
COLLID SER
WLM ENVIRONMENT DDSNSPENV
RUN OPTIONS 'NOTEST(NONE,*,*,*)'
P1: BEGIN
--Declare variables
DECLARE CONSUMER INTEGER;
DECLARE NEW_MMS_NO INTEGER;
DECLARE END_TABLE INT DEFAULT 0;
DECLARE C1 CURSOR FOR
SELECT I20_CONSUMER_ID,
NEW_MMS_NO
FROM SERDB.I20_TEMP
-- WHERE I20_CONSUMER_ID = 164921;
ORDER BY I20_CONSUMER_ID;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET END_TABLE = 1;
OPEN C1;
FETCH C1 INTO CONSUMER,
NEW_MMS_NO;
WHILE END_TABLE = 0 DO
UPDATE SERDB.I20_CONSUMER_T
SET I20_MMS_NO = NEW_MMS_NO
WHERE I20_CONSUMER_ID = CONSUMER;
END WHILE;
CLOSE C1;
END P1
The above stored procedure builds with a cond code 0, but fails to execute even when a specific consumer_id. Does anyone see something wrong?
Individual sql statements run exactly as they're supposed to.
I've followed the examples for Cursors in SQL Procedures from IBM.
Thank you

I agree 100% with #X-Zero, this seems like a huge amount of work defining cursors and what-not, when you could do a simple set-based operation (likely with better performance). Here are two examples of how you can do it with a single operation:
Normal UPDATE:
UPDATE SESSION.I20_CONSUMER_T A
SET I20_MMS_NO = (
SELECT NEW_MMS_NO
FROM SESSION.I20_TEMP B
WHERE A.I20_CONSUMER_ID = B.CONSUMER
)
WHERE EXISTS (
SELECT 1
FROM SESSION.I20_TEMP C
WHERE A.I20_CONSUMER_ID = C.CONSUMER
)
New MERGE hotness:
MERGE INTO SESSION.I20_CONSUMER_T AS T
USING SESSION.I20_TEMP AS M
ON T.I20_CONSUMER_ID = M.CONSUMER
WHEN MATCHED THEN
UPDATE SET T.I20_MMS_NO = M.NEW_MMS_NO
ELSE IGNORE
These were tested on DB2 for Linux/Unix/Windows v9.7, but should work on any version of DB2 newer than 9.1 (DB2 for iSeries is a wildcard, I never remember what that platform does or doesn't support :) )

The FETCH command must be inside the WHILE, so that each time it is invoked, it fetches a row.

Related

Convert a Set of to Integer

As a newbie in Delphi I run into a problem with an external API.
This external API expects a parameter with one or two value, I think called bitwise parameter.
In Delphi this is done by a set of
The basic is an Enumeration.
TCreateImageTask = (
citCreate = 1,
citVerify
);
This I have put into a set of:
TCreateImageTasks = set of TCreateImageTask
In a function I fill this set with:
function TfrmMain.GetImageTask: TCreateImageTasks;
begin
Result:=[];
if chkCreate.checked then Include(Result, citCreate);
if chkVerify.checked then Include(Result, citVerify);
end;
Now I have to give this Tasks to a external DLL, written in C++
The DLL expects a __int8 value. It may contain one or two TCreateImageTasks. In C++ done by:
__int8 dwOperation = 0;
if (this->IsDlgButtonChecked(IDC_CHECK_CREATE))
{
dwOperation = BS_IMGTASK_CREATE;
}
if (this->IsDlgButtonChecked(IDC_CHECK_VERIFY))
{
dwOperation |= BS_IMGTASK_VERIFY;
}
int32 res = ::CreateImage(cCreateImageParams, dwOperation);
So I have to convert my set of to an integer. I do by
function TfrmMain.SetToInt(const aSet;const Size:integer):integer;
begin
Result := 0;
Move(aSet, Result, Size);
end;
I call with
current task := GetImageTask;
myvar := SetToInt(currentTask, SizeOf(currentTask));
The problem I have now, that myvar is 6 when 2 values are inside the set, 2 if only create is inside the set and 4 if only verify is inside the set. That do not look right to me and the external DLL do not know this values.
Where is my fault?
I guess it works when you remove the = 1 in the declaration of TCreateImageTask?
The = 1 shifts the ordinal values by 1 giving the results you see, but is probably not what is needed. For that we need to know the values for BS_IMGTASK_CREATE and BS_IMGTASK_VERIFY.
My psychic powers tell me that BS_IMGTASK_CREATE = 1 and BS_IMGTASK_VERIFY = 2. Given that these are bit masks they correspond to the values 2^0 and 2^1. This matches the ordinal values 0 and 1.
Thus you should declare
TCreateImageTask = (citCreate, citVerify);
to map citCreate to 0 and citVerify to 1.
It's all about something called Bitwise Operation!
Converting a SET to LONGWORD is widely used in Delphi implementation of Windows API.
This would be what you are looking for:
How to save/load Set of Types?
This was already answered here too:
Bitwise flags in Delphi

what is the default value for IBM Informix set isolation level

1 the stored procedure
create procedure sp_count_demo(
i_user_id varchar(30)
)
returning p_count as num_of_row ;
define p_count integer ;
set isolation to dirty read ;
let p_row = 0 ;
select count(*)
into p_count
from some_table a
where a.user_id = i_user_id
;
return p_row;
end procedure ;
2 The procedure at (1) will be called from java webapps with connection pool
3 Do I need to set the isolation level back to previous value before returning the result? (ie to avoid another process reusing the connection from having "dirty read" isolation level)
4 What is the default isolation level
5 Where/How can I get the default value for isolation level
Thanks in advance
Since a connection pool is in use the stored procedure should return the isolation level to its previous setting in order to avoid unexpected results when another app uses the same connection. The default isolation level depends on the logging mode of the database:
For an unlogged database it will effectively be "Dirty Read" (shown as NL by the onstat -g ses command).
For a mode ANSI database it will be "Repeatable Read."
For other logged databases it will be "Committed Read."
The onconfig parameter USELASTCOMMITTED can also be used to change how the default isolation level is used. More information on that can be found in the Knowledge Center (search on USELASTCOMMITTED).
It is possible for a session to find out its current isolation level using a query against the sysmaster database. This query was run on Informix 12.10 but should also be valid for 11.70:
select tx.isolevel
from sysmaster:systxptab tx, sysmaster:sysrstcb r, sysmaster:sysscblst s
where s.address = r.scb and tx.owner = r.address
and s.sid = dbinfo("sessionid");
It returns the isolation level as an integer which is an internal value - for example committed read has value 2. I don't believe the mapping of isolation level to integer value is published so you will need to experiment with setting different levels for a session and then running the above query.

How can I use foreach and fork together to do something in parallel?

This question is not UVM specific but the example that I am working on is UVM related.
I have an array of agents in my UVM environment and I would like to launch a sequence on all of them in parallel.
If I do the below:
foreach (env.agt[i])
begin
seq.start(env.agt[i].sqr);
end
, the sequence seq first executes on env.agt[0].sqr. Once that gets over, it then executes on env.agt[1].sqr and so on.
I want to implement a foreach-fork statement so that seq is executed in parallel on all agt[i] sequencers.
No matter how I order the fork-join and foreach, I am not able to achieve that. Can you please help me get that parallel sequence launching behavior?
Thanks.
Update to clarify the problem I am trying to solve:
The outcome of the below code constructs is the exact same as above without the fork-join.
foreach (env.agt[i])
fork
seq.start(env.agt[i].sqr);
join
fork
foreach (env.agt[i])
seq.start(env.agt[i].sqr);
join
// As per example in § 9.3.2 of IEEE SystemVerilog 2012 standard
for (int i=0; i<`CONST; ++i)
begin
fork
automatic int var_i = i;
seq.start(env.agt[var_i].sqr);
join
end
The issue is each thread of the fork is pointing to the same static variable i. Each thread needs its own unique copy and this can be achieved with the automatic keyword.
foreach (env.agt[i])
begin
automatic int var_i = i;
fork
seq.start(env.agt[var_i].sqr);
join_none // non_blocking, allow next operation to start
end
wait fork;// wait for all forked threads in current scope to end
IEEE std 1800-2012 § 6.21 "Scope and lifetime" gives examples of the uses static and automatic. Also check out § 9.3.2 "Parallel blocks", the last example shows demonstrates parallel threads in a for-loop.
Use join_none to create new threads; § 9.3.2 "Parallel blocks", Table 9-1—"fork-join control options".
Use fork wait statement to wait for all threads in the current scope to complete; § 9.6.1 "Wait fork statement"
Example:
byte a[4];
initial begin
foreach(a[i]) begin
automatic int j =i;
fork
begin
a[j] = j;
#($urandom_range(3,1));
$display("%t :: a[i:%0d]:%h a[j:%0d]:%h",
$time, i,a[i], j,a[j]);
end
join_none // non-blocking thread
end
wait fork; // wait for all forked threads in current scope to end
$finish;
end
Outputs:
2 :: a[i:4]:00 a[j:3]:03
2 :: a[i:4]:00 a[j:0]:00
3 :: a[i:4]:00 a[j:2]:02
3 :: a[i:4]:00 a[j:1]:01
I think that the more "UVM" way to approach this is with a virtual sequence. Assuming you already have a virtual sequencer which instantiates an array of agent sequencers then the body your virtual sequence would look something like this:
fork
begin: isolation_thread
foreach(p_sequencer.agent_sqr[i])
automatic int j = i;
fork
begin
`uvm_do_on(seq, p_sequencer.agent_sqr[j]);
end
join_none
end
wait fork;
end: isolation_thread
join
This has worked for me in the past.
Greg's solution below helped me derive the solution to my UVM based problem. Here is my solution:
The below fork-join block resides in the main_phase task in a test case class. The wait fork; statement waits for all the fork statements in its scope (= the foreach_fork begin-end block) to finish before proceeding further. An important thing to note is that the wrapping fork-join around the begin-end was required for setting the scope of wait fork; to the foreach_fork block.
fork
begin : foreach_fork
seq_class seq **[`CONST]**;
foreach(env.agt[i])
begin
int j = i;
**seq[j] = seq_class::type_id::create
(.name($sformatf("seq_%0d", j)), .contxt(get_full_name()));**
fork
begin
seq[j].start(env.agt[j].sqr);
end
join_none // non-blocking thread
end
**wait fork;**
end : foreach_fork
join
Alternative solution that makes use of the in-sequence objection to delay the sim end.
begin
seq_class seq **[`CONST]**;
foreach(env.agt[i])
begin
int j = i;
**seq[j] = seq_class::type_id::create
(.name($sformatf("seq_%0d", j)), .contxt(get_full_name()));**
fork
begin
**seq[j].starting_phase = phase;**
seq[j].start(env.agt[j].sqr);
end
join_none // non-blocking thread
end
end
I realized that I also needed to create a new sequence object for each seq I wanted to run in parallel.
Thanks to Dave for making the point that the properties in System Verilog classes are automatic by default.
Note for the alternative solution:
As I didn't use wait fork; I use the UVM objections raised in the sequence itself to do the job of holding off the simulation $finish call. To enable raising of the objections in the sequences, I use the seq[j].starting_phase = phase; construct.
try with
int i = 0
foreach (env.agt)
begin
seq.start(env.agt[i].sqr);
i++;
end

How to call plpgsql functions from Ruby on rails?

How to call plpgsql functions from Ruby on Rails application?
This is the function definition:
CREATE OR REPLACE FUNCTION add_org_pincode(i_user_id integer, i_org_id integer, i_amount numeric, i_pincode_id integer)
RETURNS integer AS
$BODY$
Declare
v_org_balance numeric;
Begin
Select o.balance
Into Strict v_org_balance
From organizations o
Left Join organizations d On o.parent_organization_id = d.id
Where o.id = i_org_id
For Update Of o;
--
Insert Into org_balance_transactions(organization_id, balance, amount_added, reason, action_user_id, transaction_id)
Values (i_org_id, v_org_balance, i_amount, 10, i_user_id, i_pincode_id);
--
Update organizations
Set balance = balance + i_amount
Where id = i_org_id;
-- Other error
Return 0;
End;$BODY$
LANGUAGE 'plpgsql' VOLATILE;
So, how do I call add_org_pincode?
Like the others have said, something akin to:
result = connection.execute ("SELECT add_org_pincode(#{user_id}, #{org_id}, #{amount}, #{pincode_id};")
This is an example that I have proven, it might help you...
CREATE LANGUAGE plpgsql;
/**PROMEDIOS**/
CREATE OR REPLACE FUNCTION average(valores NUMERIC[],OUT prom NUMERIC) AS $$
DECLARE
element_count INT4;
sum numeric := 0.0;
BEGIN
element_count := array_upper(valores, 1) - array_lower(valores, 1) +1;
FOR i IN array_lower(valores, 1) .. array_upper(valores, 1)
LOOP
sum := sum + valores[i];
END LOOP;
prom := trunc(sum/element_count,1);
END;
$$ LANGUAGE plpgsql;
Obviously you have to execute this SQL in your database, just add it in your SQL Editor (pointing your database) in PGAdmin, after executing that, this function must be available like this select average(ARRAY[4.5,3.2,7.0]);.
In Rails Console I tried the example like this:
_calificaciones = Calificacion.all(:conditions => ["promedio_parciales != average(array[parcial1,parcial2,parcial3])"])
and it worked fine for me... Good luck...
You can go straight to the ActiveRecord::Base.connection to execute SQL which is what I did for some prototype code...
result = ActiveRecord::Base.connection.execute("SELECT * FROM stock_hourly_performance(#{stock_id})")
I don't know if its the best solution to the problem but it got me up and running.
You can execute raw SQL with:
ActiveRecord::Base.connection.execute('SQL goes here')
Be sure that it is absolutely necessary to bypass ActiveRecord (Rail's ORM).

mysql stored procedure: using declared vars in a limit statement returns an error

I have the following code:
delimiter ;
DROP PROCEDURE IF EXISTS ufk_test;
delimiter //
CREATE PROCEDURE ufk_test(IN highscoreChallengeId INT UNSIGNED)
BEGIN
DECLARE vLoopOrder INT UNSIGNED DEFAULT 5;
DECLARE vLoopLimit INT UNSIGNED DEFAULT 10;
select * from fb_user LIMIT vLoopOrder,vLoopLimit;
END//
delimiter ;
Mysql returns the following error:
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'vLoopOrder,vLoopLimit;
END' at line 11
it seems that I cannot use declared variables in a LIMIT statement. is there any other way to overcome this ?
of course this is a simple example, here i could just put static numbers but I need to know if it's possible in any way to use any kind of variables with LIMIT.
Thanks
i use something like:
SET #s = CONCAT('SELECT * FROM table limit ', vLoopOrder ', ', vLoopLimit);
PREPARE stmt1 FROM #s;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;

Resources