I have a temp table ttsales which contains a field name t_sales of integer type, I want to find out the maximum and minimum value from a t_sales And on a button click those maximum and minimum value will show. what can I do?
One possibility is to sort the temp-table in ascending and descending order. Adding an index will help. If you cant change the temp-table and its large this might not be the best solution.
DEFINE TEMP-TABLE ttsales NO-UNDO
FIELD t_sales AS INTEGER
INDEX sales t_sales.
DEFINE VARIABLE iMin AS INTEGER NO-UNDO.
DEFINE VARIABLE iMax AS INTEGER NO-UNDO.
CREATE ttsales.
ASSIGN ttsales.t_sales = 10.
CREATE ttsales.
ASSIGN ttsales.t_sales = 1.
CREATE ttsales.
ASSIGN ttsales.t_sales = 130.
CREATE ttsales.
ASSIGN ttsales.t_sales = 2.
/* Get minimum value */
FOR EACH ttsales BY ttsales.t_sales:
iMin = ttsales.t_sales.
LEAVE.
END.
/* Get maximum value */
FOR EACH ttsales BY ttsales.t_sales DESCENDING:
iMax = ttsales.t_sales.
LEAVE.
END.
DISPLAY iMin iMax.
DEFINE QUERY qry FOR tsales.
OPEN QUERY qry FOR EACH tsales BY t_sales.
GET FIRST qry.
MESSAGE tsales.t_sales.
GET LAST qry.
MESSAGE tsales.t_sales.
Depending on size of table, add an index on the field.
Related
Is it good if we use lookup function in for each where clause? Will it cause performance issue? Please help to understand and provide the example how to avoid.
define variable cGroupID as character no-undo.
for each <table> no-lock where lookup(cGroupID,<table.fieldname>) <> 0:
**do something...**
end.
note - table field name can have multiple comma separated group
A lookup function cannot use an index, so yes you can introduce sub-par performance which can be avoided. See the following example using the sports database which will read all records when using lookup and will limit the set to what meets the criteria when breaking the parts out to individual query parts:
def var ii as int no-undo.
def var iread as int64 no-undo.
function reads returns int64:
find _file where _file._file-name = 'customer' no-lock.
find _tablestat where _tablestat._tablestat-id = _file._file-number no-lock.
return _tablestat._tablestat-read.
end function.
iread = reads().
for each customer
where lookup( customer.salesrep, 'dkp,sls' ) > 0
no-lock:
ii = ii + 1.
end.
message 'lookup - records:' ii 'read:' reads() - iread.
ii = 0.
iread = reads().
for each customer
where customer.salesrep = 'dkp'
or customer.salesrep = 'sls'
no-lock:
ii = ii + 1.
end.
message 'or - records:' ii 'read:' reads() - iread.
https://abldojo.services.progress.com/?shareId=6272e1223fb02369b2545bf4
Your example however seems to be performing a reverse lookup, ie the database field contains a comma separated list of values, which seems like not obeying the basic rules of database normalization.
If you want to keep the list in a single field, adding a word index on your comma separated field may help. You can then use contains.
I am using following dynamic query to fetch the data from a table. But I am getting compilation error "Phrase or option conflicts with previous phrase or option. (277)". Not sure where I am making mistakes and how to fix it. Please help me modifying the below example query.
define variable hbuffer as handle no-undo.
define variable hQuery as handle no-undo.
define variable cQuery as character no-undo.
define temp-table tt_table no-undo
field tt_week1 as character label "Week1"
.
create buffer hbuffer for table "<table>".
cQuery = "for each <table> no-lock ".
create query hQuery.
hQuery:set-buffers(hbuffer).
cQuery = cQuery + ":".
hQuery:query-prepare(cQuery).
hQuery:query-open().
if hQuery:query-open() then
do:
do while hQuery:get-next():
create tt_table.
assign tt_week1 = hbuffer::qty[1] /*field name qty data type is deci-10[52].*/
.
end.
end.
for each tt_table :
disp tt_week1.
end.
As pointed out by Mike, your attempt to reference the extent is throwing the error, a dynamic extent reference uses round parentheses (and works fine with shorthand):
hbuffer::qty(1)
Additionally:
you do not need to terminate your query with a :
get-next() defaults to no-lock
you are opening your query twice
// some demo data
define temp-table db no-undo
field qty as decimal extent 7
.
create db. db.qty[1] = 1.
create db. db.qty[1] = 2.
// the question
define variable hb as handle no-undo.
define variable hq as handle no-undo.
define variable cquery as character no-undo.
define temp-table tt no-undo
field week1 as character label 'Week1'
.
create buffer hb for table 'db'.
cquery = substitute( 'for each &1', hb:name ).
create query hq.
hq:set-buffers( hb ).
if hq:query-prepare( cquery ) and hq:query-open() then do:
do while hq:get-next():
create tt.
tt.week1 = hb::qty(1). // <-- round parentheses
end.
end.
for each tt:
display tt.week1.
end.
https://abldojo.services.progress.com/?shareId=626aff353fb02369b2545434
The compile error should tell you the line the error is coming from.
In the code snippet, you haven't define a tt_week field anywhere.
In general, if you want to assign a (temp)table field, you should use the table.field notation; the AVM can often figure out your intent, but not being specific is error-prone.
The problem is with the shorthand syntax here:
hbuffer::qty[1]
If you replace this with:
hbuffer:buffer-field ("qty"):BUFFER-VALUE (1)
it'll work (until the point, that Peter made with the undefined field tt_week1. I did not find any reference saying if or if not the shorthand syntax should work with EXTENT fields. It may be worth checking that with Progress tech-support.
So this will bring you further:
assign tt_data = hbuffer:buffer-field ("qty"):BUFFER-VALUE (1) /*field name qty data type is deci-10[52].*/
I am new to progress 4GL. I have written logic for buffer by adding multiple tables into a single query and finding the records from another table by using table field names. I am not sure why I am getting errors. Please help to change the logic
DEFINE VARIABLE ix AS INTEGER NO-UNDO.
DEFINE VARIABLE qh AS HANDLE NO-UNDO.
DEFINE VARIABLE bh AS HANDLE NO-UNDO.
DEFINE VARIABLE fh AS HANDLE NO-UNDO EXTENT 10.
DEFINE VARIABLE cQuery AS CHARACTER NO-UNDO.
CREATE BUFFER bh FOR TABLE "Customer, Invoice".
CREATE QUERY qh.
ASSIGN
cQuery = "FOR EACH Customer NO-LOCK, EACH Invoice WHERE Invoice.Cust-Num = Customer.Cust-
Num NO-LOCK: ".
qh:SET-BUFFERS(bh).
qh:QUERY-PREPARE(cQuery).
qh:QUERY-OPEN().
qh:GET-FIRST().
/* Field Invoice.Cust-Num is already defined in cQuery*/
FIND Order WHERE Order.Cust-Num = Invoice.Cust-Num NO-LOCK NO-ERROR.
IF NOT AVAILABLE Order THEN DO:
FIND Ref-Call WHERE Ref-Call.Cust-Num = Invoice.Cust-Num NO-LOCK NO-ERROR.
DISPLAY Ref-Call.Cust-Num.
END.
qh:QUERY-CLOSE().
bh:BUFFER-RELEASE().
DELETE OBJECT bh.
DELETE OBJECT qh.
Looking at the field names, you're using the classic Sports Database for your training. There's a "newer" Sports2000 demo database with a little bit more of data that might be worth playing with.
There are multiple issues in that program.
First, you cannot define a single dynamic buffer for two tables (Customer, Invoice). This would be causing an error at runtime. You need to:
DEFINE VARIABLE bh1 AS HANDLE NO-UNDO.
DEFINE VARIABLE bh2 AS HANDLE NO-UNDO.
CREATE BUFFER bh1 FOR TABLE "Customer".
CREATE BUFFER bh2 FOR TABLE "Invoice".
and then
qh:ADD-BUFFER(bh1).
qh:ADD-BUFFER(bh2).
The second issue (your compile error), is because the compiler does not see that you're accessing the table Invoice already. bh2 will only at runtime be known to be a buffer for the Invoice table. So you need to access the Cust-Num field dynamically:
FIND Order WHERE Order.Cust-Num = bh2::Cust-Num NO-LOCK NO-ERROR.
Note: You're accessing a single Order by the Cust-Num of the invoice here - I assume, you want to do something like accessing the Order by the Invoice's Order-Num field. That would be a logical mistake, not a syntax error.
However, nothing in your program justifies the need for a dynamic query. That just adds in this case unneeded complexity. You program is not yet iterating the records in the dynamic-query qh - but I assume that's the goal. So this simple static FOR EACH block does the same:
FOR EACH Customer NO-LOCK, EACH Invoice WHERE Invoice.Cust-Num = Customer.Cust-
Num NO-LOCK:
FIND Order WHERE Order.Cust-Num = Invoice.Cust-Num NO-LOCK NO-ERROR.
IF NOT AVAILABLE Order THEN DO:
FIND Ref-Call WHERE Ref-Call.Cust-Num = Invoice.Cust-Num NO-LOCK NO-ERROR.
DISPLAY Ref-Call.Cust-Num.
END /* NOT AVAILABLE */.
END. /* FOR EACH */
Lastly, here:
DELETE OBJECT bh.
DELETE OBJECT qh.
DELETE OBJECT statements belong by their nature into a FINALLY
block.
You need to check validity of the handles before deleting
them:
FINALLY:
IF VALID-HANDLE (bh1) THEN DELETE OBJECT bh1.
IF VALID-HANDLE (bh2) THEN DELETE OBJECT bh2.
IF VALID-HANDLE (qh) THEN DELETE OBJECT qh.
END.
Here is a working example - my changes to your code can generally be identified by being in lower case.
DEFINE VARIABLE qh AS HANDLE NO-UNDO.
DEFINE VARIABLE bhc AS HANDLE NO-UNDO.
DEFINE VARIABLE bhi AS HANDLE NO-UNDO.
DEFINE VARIABLE cQuery AS CHARACTER NO-UNDO.
CREATE BUFFER bhc FOR TABLE "Customer".
create buffer bhi for table "Invoice".
CREATE QUERY qh.
cQuery = "FOR EACH Customer NO-LOCK,"
+ "EACH Invoice WHERE Invoice.CustNum = Customer.CustNum no-lock".
qh:SET-BUFFERS(bhc,bhi).
qh:QUERY-PREPARE(cQuery).
qh:QUERY-OPEN().
do while qh:get-next().
message bhc::CustNum bhi::InvoiceNum.
FIND Order WHERE Order.CustNum = bhi::CustNum NO-LOCK NO-ERROR.
IF NOT AVAILABLE Order THEN DO:
FIND RefCall WHERE RefCall.CustNum = bhi::CustNum NO-LOCK NO-ERROR.
if available RefCall then
message RefCall.CustNum.
END.
end.
finally:
DELETE OBJECT bhc no-error.
DELETE OBJECT bhi no-error.
DELETE OBJECT qh no-error.
end finally.
Watch the example run in ABLdojo.
I have written a program for matching one records to another records in FOR Each loop but i don't know how to give an error message if none of the records are matching. Let me share my codes
DEFINE VARIABLE cPos AS INTEGER NO-UNDO.
DEFINE TEMP-TABLE tt_data NO-UNDO
FIELD cPosition AS CHARACTER FORMAT "X(60)"
FIELD cEndCode AS CHARACTER
FIELD cShotCode AS CHARACTER.
/*so many records are stored in tt_data and below is one of the records for your understanding*/
CREATE tt_data.
ASSIGN
tt_data.cPosition ="S$$$^^^^^^^^^^$$$^^^MC^^^^^^^^^^^^R^^^^^^^^^^^^^^^^^^^^^^^^^"
tt_data.cEndCode = 10
tt_data.cShotCode = "S".
cPos = integer( tt_data.cEndCode / 10 ).
/* Consider 60 records available in tt_data */
FOR EACH tt_data.
FIND FIRST tt_date WHERE tt_data.cShotCode =
SUBSTRING(tt_data.cPosition,cPos,1) NO-LOCK NO-ERROR.
DISPLAY tt_data.cShotCode. /* Displayed Value is S */
IF NOT AVAILABLE tt_date THEN
MESSAGE "NONE OF THE RECORDS MATCHING WITH tt_data.cPosition "
LEAVE.
END.
I can get at least one record matching in tt_data. Here the problem is I don't want to LEAVE if any one record is matching but I want to get an error message with LEAVE statement if none of the records are matching. Could you please help this case?
I think this might be what you are trying to do:
DEFINE VARIABLE cPos AS INTEGER NO-UNDO.
DEFINE TEMP-TABLE tt_data NO-UNDO
FIELD cPosition AS CHARACTER FORMAT "X(60)"
FIELD cEndCode AS CHARACTER
FIELD cShotCode AS CHARACTER.
/*so many records are stored in tt_data and below is one of the records for your understanding*/
CREATE tt_data.
ASSIGN
tt_data.cPosition ="S$$$^^^^^^^^^^$$$^^^MC^^^^^^^^^^^^R^^^^^^^^^^^^^^^^^^^^^^^^^"
tt_data.cEndCode = 10
tt_data.cShotCode = "S".
cPos = integer( tt_data.cEndCode / 10 ).
/* Consider 60 records available in tt_data */
FOR EACH tt_data: /* although it 'works', "." is not appropriate, FOR EACH should end with a ":" */
FIND FIRST tt_date WHERE tt_data.cShotCode = SUBSTRING(tt_data.cPosition,cPos,1) NO-LOCK NO-ERROR.
/* changes start here */
IF AVAILABLE tt_date THEN
do:
DISPLAY tt_data.cShotCode. /* Displayed Value is S */ /* only display this when it is available! */
end.
else
do:
MESSAGE "NONE OF THE RECORDS MATCHING WITH tt_data.cPosition ". /* a "." was missing */
/* LEAVE. */
end.
END.
I would personally try to do the error check before you ever get in the FOR EACH block. Sometimes you can't, but based on your sample code I think you could examine the temp-table first and provide an error message. But I'm not completely sure what you are going for based on the sample.
DEFINE VARIABLE cPos AS INTEGER NO-UNDO.
DEFINE TEMP-TABLE tt_data NO-UNDO
FIELD cPosition AS CHARACTER FORMAT "X(60)"
FIELD cEndCode AS CHARACTER
FIELD cShotCode AS CHARACTER.
/*so many records are stored in tt_data and below is one of the records for your understanding*/
CREATE tt_data.
ASSIGN
tt_data.cPosition ="S$$$^^^^^^^^^^$$$^^^MC^^^^^^^^^^^^R^^^^^^^^^^^^^^^^^^^^^^^^^"
tt_data.cEndCode = 10
tt_data.cShotCode = "S".
cPos = integer( tt_data.cEndCode / 10 ).
/* Add check here */
IF can-find( FIRST tt_date WHERE
tt_data.cShotCode = SUBSTRING(tt_data.cPosition,cPos,1 ) )
THEN
message "ERROR message".
ELSE DO:
/* Consider 60 records available in tt_data */
FOR EACH tt_data.
/* DO thing */
END.
END.
I have an orderline table which has QTY field and I would like to group items the way it is shown in image.
I tried using this:
FOR EACH RobotOrderline NO-LOCK
BREAK BY RobotOrderline.OrderedQty.
DISPLAY RobotOrderline.OrderedQty.
END.
But even the grouping seems wrong.
You need to actually sum the quantities. Also the for each still runs for each row in the table, it only orders them and sets flag for different locations in the order (first, last, first-of, last-of).
DEFINE VARIABLE iQty AS INTEGER NO-UNDO.
FOR EACH RobotOrderline NO-LOCK
BREAK BY RobotOrderline.OrderedQty.
iQty = iQty + RobotOrderline.OrderedQty.
IF LAST-OF(RobotOrderline.OrderedQty) THEN DO:
DISPLAY RobotOrderline.OrderedQty iQty.
iQty = 0.
END.
END.