PowerBuilder 12.5 sql cursors transaction size error - sql

i have a major problem and trying to find a workaround. I have an application in PB12.5 that works on both sql and oracle dbs.. (with a lot of data)
and i m using CURSOR at a point,, but the aplications crashes only in sql. Using debuging in PB i found that the sql connection returs -1 due to huge transaction size. But i want to fetch row by row my data.. is any work around to fetch data like paging?? i mean lets fetch the first 1000 rows next the other 1000 and so on.. i hope that you understand what i want to achieve (to break the fetch process and so to reduce the transaction size if possible) , here is my code
DECLARE trans_Curs CURSOR FOR
SELECT associate_trans.trans_code
FROM associate_trans
WHERE associate_trans.usage_code = :ggs_vars.usage ORDER BY associate_trans.trans_code ;
OPEN trans_Curs;
FETCH trans_Curs INTO :ll_transId;
DO WHILE sqlca.sqlcode = 0
ll_index += 1
hpb_1.Position = ll_index
if not guo_associates.of_asstrans_updatemaster( ll_transId, ls_error) then
ROLLBACK;
CLOSE trans_Curs;
SetPointer(Arrow!)
MessageBox("Update Process", "Problem with the update process on~r~n" + sqlca.sqlerrtext)
cb_2.Enabled = TRUE
return
end if
FETCH trans_Curs INTO :ll_transId;
LOOP
CLOSE trans_Curs;

Since the structure of your source table s not fully presented, I'll make some assumptions here.
Let's assume that the records include a unique field that can be used as a reference (could be a counter or a timestamp). I'll assume here that the field is a timestamp.
Let's also assume that PB accepts cursors with parameters (not all solutions do; if it does not, there are simple workarounds).
You could modify your cursor to be something like:
[Note: I'm assuming also that the syntax presented here is valid for your environment; if not, adaptations are simple]
DECLARE TopTime TIMESTAMP ;
DECLARE trans_Curs CURSOR FOR
SELECT ots.associate_trans.trans_code
FROM ots.associate_trans
WHERE ots.associate_trans.usage_code = :ggs_vars.usage
AND ots.associate_trans.Timestamp < TopTime
ORDER BY ots.associate_trans.trans_code
LIMIT 1000 ;
:
:
IF (p_Start_Timestamp IS NULL) THEN
TopTime = CURRENT_TIMESTAMP() ;
ELSE
TopTime = p_Start_Timestamp ;
END IF ;
OPEN trans_Curs;
FETCH trans_Curs INTO :ll_transId;
:
:
In the above:
p_Start_Timestamp is a received timestamp parameter which would initially be empty and then will contain the OLDEST timestamp fetched in the previous invocation,
CURRENT_TIMESTAMP() is a function of your environment returning the current timestamp.
This solution will work solely when you need to progress in one direction (i.e. from present to past) and that you are accumulating all the fetched records in an internal buffer in case you need to scroll up again.
Hope this makes things clearer.

First of all thank you FDavidov for your effort, so i managed to do it using dynamic datastore instead of cursor,, so here is my solution in case someone else need this.
String ls_sql, ls_syntax, ls_err
Long ll_row
DataStore lds_info
ls_sql = "SELECT associate_trans.trans_code " &
+ " FROM associate_trans " &
+ " WHERE associate_trans.usage_code = '" + ggs_vars.usage +"' "&
+ " ORDER BY associate_trans.trans_code"
ls_syntax = SQLCA.SyntaxFromSQL( ls_sql, "", ls_err )
IF ls_err <> '' THEN
MessageBox( 'Error...', ls_err )
RETURN
END IF
lds_info = CREATE DataStore
lds_info.Create( ls_syntax, ls_err )
lds_info.SetTransObject( SQLCA )
lds_info.Retrieve( )
DO WHILE sqlca.sqlcode = 0 and ll_row <= ll_count
FOR ll_row = 1 TO ll_count
ll_transId = lds_info.GetItemNumber( ll_row, 'trans_code' )
ll_index += 1
hpb_1.Position = ll_index
do while yield(); loop
if not guo_associates.of_asstrans_updatemaster( ll_transId, ls_error) then
ROLLBACK;
DESTROY lds_info
SetPointer(Arrow!)
MessageBox("Update Process", "Problem with the update process on~r~n" + sqlca.sqlerrtext)
cb_2.Enabled = TRUE
return
end if
NEXT
DESTROY lds_info
LOOP

Related

SSMS Set Statistics Time On/Off prints multiple lines instead of just one

When I run my query, it prints multiple execution times instead of just one. I only want one so what do I need to do to get this to only print one time stamp?
SET STATISTICS TIME ON
DECLARE #firstNum INT, #secondNum INT, #thirdNum INT, #evenSum INT
SET #firstNum = 1
SET #secondNum = 2
set #thirdNum = 2
SET #evenSum = 2
WHILE (#thirdNum <= 4000000)
BEGIN
SET #thirdNum = #firstNum + #secondNum
SET #firstNum = #secondNum
SET #secondNum = #thirdNum
IF (#thirdNum % 2) = 0
SET #evenSum += #thirdNum
END
PRINT 'Answer = ' + CONVERT(VARCHAR, #evenSum)
SET STATISTICS TIME OFF
If you remove PRINT 'Answer = ' + CONVERT(VARCHAR, #evenSum) from your code
then it won't print multiple execution time.
Here is the example of it.
Statistic time will print for each execution. Since you are looping you are performing multiple query statements (I believe this also applies for sets -i could be wrong though), each will provide an execution time. There really is no way to modify statistic time for what I think you are looking for.
You could declare a datetime2 and store the start time in the beginning by using SysDateTime, and get the end time upon completion, thus printing the difference onto the screen using DateDiff. (This will achieve what you are asking for. )
You could also look into client statistic (but that might not be what you want).
Side note and irrelevant: you are looping queries. This is not profient and you may be doing this to learn. I would recommend looking into Tally tables for replacing loops. You can greatly improve performance if you design your query correctly.

PL/SQL: ORA-00920: invalid relational operator

I get this error
23/112 PL/SQL: ORA-00920: invalid relational operator
It's pointing to AND CURRENT OF statement..
CREATE OR replace PROCEDURE alga_uz_pasirodyma(grupe_id in grupes.id%TYPE, alga in out number)
IS
v_kliento_id nariai.asm_kodas%TYPE;
TYPE bendras IS RECORD (
alga number
);
globalus bendras;
CURSOR c_klientai IS
SELECT nariai.asm_kodas
FROM nariai
where nariai.fk_grupe = grupe_id
FOR UPDATE OF nariai.alga;
BEGIN
globalus.alga:= alga;
IF grupe_id <= 0 THEN
raise_application_error(-20101, 'Nepavyko surasti grupes');
END IF;
OPEN c_klientai;
LOOP
FETCH c_klientai INTO v_kliento_id;
EXIT WHEN c_klientai%NOTFOUND;
UPDATE nariai set nariai.alga = nariai.alga * globalus.alga where nariai.asm_kodas = v_kliento_id AND CURRENT OF c_klientai;
END LOOP;
UPDATE grupes set grupes.pasirodymu_kiekis = grupes.pasirodymu_kiekis + 1 where grupes.id = grupe_id;
SELECT max(nariai.alga) into alga from nariai where nariai.fk_grupe = grupe_id;
CLOSE c_klientai;
END alga_uz_pasirodyma;
What should I do? I believe everything is declared correctly in the where statement..
"CURRENT OF" should be by itself in the where clause. It allows you to update or delete the record at the current loop iteration of the cursor.
On a different note, I don't see you doing anything significant in the loop to warrant a cursor. Ignore this note if that will change, otherwise just run the update "where nariai.fk_grupe = grupe_id"

How to get rid of Cursor and use UPDATE with SELECT

I believe that the cursor used in this code is the reason for some major performance issues, however I am new to TSQL.
Following script runs on SQL SERVER 2008. I am trying to redo it so I use JOIN statements instead, however I have not been able to do so successfully.
DECLARE AIRAMSDET CURSOR FOR
SELECT BILL, RECIEPT, NAME
FROM Client_Table
WHERE IsProcessed = 1
AND TYPE IN ('Sub','First_Time','Old') AND LEN(BILL) > 1
OPEN AIRAMSDET
FETCH AIRAMSDET into #VARBILL, #VARRECIEPT, #VARNAME
WHILE ##Fetch_Status = 0
BEGIN
UPDATE archieve
SET entry = left(#VARBILL + '- '+ #VARNAME)
WHERE archiveID = #VARBILL
END
It should be something like following
UPDATE ARCHIEVE
SET ENTRY = CT.BILL + '-' + CT.NAME
FROM CLIENT_TABLE CT
WHERE
ARCHIEVE.ARCHIVEID = CT.BILL
AND CT.ISPROCESSED = 1
AND CT.TYPE IN ('Sub','First_Time','Old') AND LEN(BILL) > 1
I have not included LEFT() as its use in your query wasn't very clear. Left takes an integer_expression as its second parameter while you are passing ##VARNAME which most likely is a VARCHAR. Please add that as you deem fit.

DB2 SQL Web Pagination - How to tell I reach EOF

Friends,
I am trying to find a very simple solution to tell me I have reach the End of file with a Web Pagination, using Fetch Next. I am using Previous & Next button to trigger stored procedure.
**FREE
// RFC Main Grid
CTL-OPT NOMAIN OPTION (*SRCSTMT : *NODEBUGIO);
DCL-PROC PUR027 EXPORT;
DCL-PI PUR027 EXTPROC(*DCLCASE);
StartingRow PACKED(3:0);
NbrOfRows PACKED(3:0);
TotalRows CHAR(10);
RowCount CHAR(10);
Search CHAR(30);
EndOfFile CHAR(3);
BOF CHAR(1);
EOF CHAR(1);
RSL CHAR(2);
END-PI;
IF Search = '';
EXEC SQL
Declare RSCURSOR cursor for
SELECT CDEPT, CDESC, ROW_NUMBER() OVER(ORDER BY CDESC, CDEPT) as ROWNUMBER
FROM CDPL03
ORDER BY CDESC, CDEPT
OFFSET (:StartingRow - 1) * :NbrOfRows ROWS
FETCH NEXT :NbrOfRows ROWS ONLY;
EXEC SQL Open RSCURSOR;
EXEC SQL SET RESULT SETS Cursor RSCURSOR;
ELSE;
EXEC SQL
Declare RSCURSOR2 cursor for
SELECT CDEPT, CDESC, ROW_NUMBER() OVER(ORDER BY CDESC, CDEPT) as ROWNUMBER
FROM CDPL03
WHERE CDESC LIKE '%' concat trim(:Search) concat '%' OR
CDEPT LIKE '%' concat trim(:Search) concat '%'
ORDER BY CDESC, CDEPT
OFFSET (:StartingRow - 1) * :NbrOfRows ROWS
FETCH NEXT :NbrOfRows ROWS ONLY;
EXEC SQL Open RSCURSOR2;
EXEC SQL SET RESULT SETS Cursor RSCURSOR2;
ENDIF;
// Begin & End of File
IF StartingRow = 1;
BOF = '1';
EOF = '0';
ELSE;
BOF = '0';
EOF = '0';
ENDIF;
// Validate for SQL errors
IF SQLSTATE = '00000';
RSL = '00';
//TotalRows2 = %CHAR(TotalRows);
ELSEIF SQLSTATE = '02000';
RSL = '10';
ELSE;
RSL = '20';
ENDIF;
RETURN;
END-PROC PUR027;
// To create the service program:
// CRTSRVPGM SRVPGM(BPCSO/PUR027WS)
// MODULE(BPCSO/PUR027W)
// SRCFILE(BPCSS/PURBNDF) SRCMBR(PUR027WB)
When reading multiple records in a block, I retrieve the number of records fetched with GET DIAGNOSTICS like this:
exec sql get diagnostics
:cnt = row_count;
Then if the number of records fetched is less than the requested number of records, I know that I am on the last page.
There is a problem with this method though. If the last page is full, you don't know it until you try to read the next page, and it is empty. So one way to handle that is to request one record more than you are going to present on the page. That is, if you are presenting 25 records per page, request 26. If your result set has 26 records, then there is at least one record on the next page. Still only present 25 records, and increment your offset by 25 records each time, just request 26 records. If the record set has less than 26 records, then you know you are on the last page.
Take a look at SQLERRD(2)
For an OPEN statement, if the cursor is insensitive to changes, SQLERRD(2) contains the actual number of rows in the result set. If the cursor is sensitive to changes, SQLERRD(2) contains an estimated number of rows in the result set.
You can also use GET DIAGNOSTICS after the open for the same info...
DB2_NUMBER_ROWS
If the previous SQL statement was an OPEN or a FETCH which caused the size of the result table to be known, returns the number of rows in the result table. For SENSITIVE cursors, this value can be thought of as an approximation since rows inserted and deleted will affect the next retrieval of this value. Otherwise, the value zero is returned.
Key point for both, for an exact count, you'd need to declare your cursor INSENSITIVE which will create a copy of your selected rows so that inserts, deletes and updates don't affect the results. There's also a performance hit.

Stored procedure to find next and previous row in SQL Server 2005

Right now I have this code to find next and previous rows using SQL Server 2005. intID is the gallery id number using bigint data type:
SQL = "SELECT TOP 1 max(p.galleryID) as previousrec, min(n.galleryID) AS nextrec FROM gallery AS p CROSS JOIN gallery AS n where p.galleryid < '"&intID&"' and n.galleryid > '"&intID&"'"
Set rsRec = Server.CreateObject("ADODB.Recordset")
rsRec.Open sql, Conn
strNext = rsRec("nextrec")
strPrevious = rsRec("previousrec")
rsRec.close
set rsRec = nothing
Problem Number 1:
The newest row will return nulls on the 'next record' because there is none. The oldest row will return nulls because there isn't a 'previous record'. So if either the 'next record' or 'previous record' doesn't exist then it returns nulls for both.
Problem Number 2:
I want to create a stored procedure to call from the DB so intid can just be passed to it
TIA
This will yield NULL for previous on the first row, and NULL for next on the last row. Though your ordering seems backwards to me; why is "next" lower than "previous"?
CREATE PROCEDURE dbo.GetGalleryBookends
#GalleryID INT
AS
BEGIN
SET NOCOUNT ON;
;WITH n AS
(
SELECT galleryID, rn = ROW_NUMBER()
OVER (ORDER BY galleryID)
FROM dbo.gallery
)
SELECT
previousrec = MAX(nA.galleryID),
nextrec = MIN(nB.galleryID)
FROM n
LEFT OUTER JOIN n AS nA
ON nA.rn = n.rn - 1
LEFT OUTER JOIN n AS nB
ON nB.rn = n.rn + 1
WHERE n.galleryID = #galleryID;
END
GO
Also, it doesn't make sense to want an empty string instead of NULL. Your ASP code can deal with NULL values just fine, otherwise you'd have to convert the resulting integers to strings every time. If you really want this you can say:
previousrec = COALESCE(CONVERT(VARCHAR(12), MIN(nA.galleryID)), ''),
nextrec = COALESCE(CONVERT(VARCHAR(12), MAX(nB.galleryID)), '')
But this will no longer work well when you move from ASP to ASP.NET because types are much more explicit. Much better to just have the application code be able to deal with, instead of being afraid of, NULL values.
This seems like a lot of work to get the previous and next ID, without retrieving any information about the current ID. Are you implementing paging? If so I highly recommend reviewing this article and this follow-up conversation.
Try this (nb not tested)
SELECT TOP 1 max(p.galleryID) as previousrec, min(n.galleryID) AS nextrec
FROM gallery AS p
CROSS JOIN gallery AS n
where (p.galleryid < #intID or p.galleryid is null)
and (n.galleryid > #intID or n.galleryid is null)
I'm assuming you validate that intID is an integer before using this code.
As for a stored procedure -- are you asking how to write a stored procedure? If so there are many tutorials which are quite good on the web.
Since Hogan contributed with the SQL statement, let me contribute with the stored proc part:
CREATE PROCEDURE spGetNextAndPreviousRecords
-- Add the parameters for the stored procedure here
#intID int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT TOP 1 max(p.galleryID) as previousrec, min(n.galleryID) AS nextrec
FROM gallery AS p
CROSS JOIN gallery AS n
where (p.galleryid < #intID or p.galleryid is null)
and (n.galleryid > #intID or n.galleryid is null)
END
And you call this from code as follows (assuming VB.NET):
Using c As New SqlConnection(ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString)
c.Open()
Dim command = New SqlCommand("spGetNextAndPreviousRecords")
command.Parameters.AddWithValue("#intID", yourID)
Dim reader as SqlDataReader = command.ExecuteReader()
While(reader.Read())
' read the result here
End While
End Using