Adding more "AND" conditions to FOR LAST causing performance issue - openedge

I'm new to progress 4GL. I'm trying to find last record from a table. But its causing performance issue. I directly copied my query here so it should be syntax error. Please help me to modify the logic or give me suggestion.
Note - Syntax(USE-SYNTAX) available only for following fields but not sure adding this to for last is good idea.
pc_domain,
pc_list_classification,
pc_list,
pc_curr,
pc_prod_line,
pc_part,
pc_um,
pc_start
for last pc_mstr no-lock
where pc_domain = global_domain
and pc_list_classification = 1
and pc_curr <> ""
and pc_part = b_ps_mstr.ps_comp
and pc_um <> ""
and (pc_start <= v_end[v_i]
or pc_start = ?)
and (pc_expire >= v_end[v_i]
or pc_expire = ?)
and (pc_amt_type = "L"
or pc_amt_type = "P"):
end.
if not available pc_mstr then
do:
for last pc_mstr no-lock
where pc_domain = global_domain
and pc_list_classification = 1
and pc_curr <> ""
and pc_part = b_ps_mstr.ps_comp
and pc_um <> ""
and (pc_amt_type = "L"
or pc_amt_type = "P"):
end.
end.

What do you mean with last? Do you mean LAST as in what Progress means:
LAST Uses the criteria in the record-phrase to find the last record in the table that meets that criteria. The AVM finds the last record before sorting.
Or do you mean something else? Like the last record created? Depending on what you mean you might have to do different things.
Some pointers about performance though:
Basically where clauses using = is good, >, <, >=, <=, BEGINS etc is decent and <>, NOT is BAD.
But it also boils down to what index you can use. You need to know about the indices of the table! But regardless of indices: those <> will make you unhappy. They will cause "table scans" (the entire table will be read).
<> "" could perhaps be replaced with > "" in this case - a little less evil.
Also you need to use () in a better way with those or's. Otherwise you might not get what you want. OR will win over AND so A AND B OR C really is run as (A AND B) OR C. Maybe you really ment A AND (B OR C) - in that case you need to use those ( ) wisely.

Related

How to find the minimum value for 1-many relationship?

I want to find the minimum date value in a list of transactions that are associated with an investment. There are many transactions for one investment, clearly. How do I write this so that Progress will only give me the minimum transaction date? I get the minimum at the end of my list, but I do not want the list, just the minimum value.
FOR EACH ilinvest WHERE ilinvest.inv-num EQ 406885:
FOR EACH iltrans WHERE iltrans.reg-pin EQ ilinvest.reg-pin:
DISPLAY iltrans.tran-dt(MINIMUM).
END.
END.
If you have an index on the tran-dt field, you could do something like
FOR EACH ilinvest WHERE ilinvest.inv-num EQ 406885:
FOR EACH iltrans WHERE iltrans.reg-pin EQ ilinvest.reg-pin
BY iltrans.tran-dt ASCENDING:
// The iltrans.tran-dt value here is the lowest. Note that
// you may see the unknown value .
// Leave after getting the first record
LEAVE.
END.
END.
Thank you both for your help. Removing the ascending worked like a charm:
FOR EACH ilinvest:
FOR EACH iltrans WHERE iltrans.reg-pin EQ ilinvest.reg-pin
AND iltrans.acct-num EQ ilinvest.inv-num
BY iltrans.tran-dt:
iMin = iltrans.tran-dt.
LEAVE.
END.
END.
Beware if your date field has no value, ie the unknown value (?). If the unknown value sorts before or after other values depends on all sorts of black magic
Additionally, since I do not like leave, I prefer a while:
def var dt min as date no-undo.
for each ilinvest no-lock,
each iltrans
where iltrans.reg-pin = ilinvest.reg-pin
and iltrans.acct-num = ilinvest.inv-num
no-lock
by iltrans.tran-dt
while dtmin = ?:
if iltrans.trans-dt <> ? then
dtmin = iltrans.tran-dt.
end.

Does a OUTER-JOIN always divide the query in two parts, leaving the part on the right empty if not complete in Progress?

I'm trying to do an OUTER-JOIN in progress using this page as inspiration. My code is as follows
OPEN QUERY qMovto
FOR EACH movto-estoq
WHERE movto-estoq.lote BEGINS pc-lote
AND movto-estoq.it-codigo BEGINS pc-it-codigo
AND movto-estoq.dt-trans >= pd-data1
AND movto-estoq.dt-trans <= pd-data2
AND movto-estoq.cod-emitente = pi-cod-emitente,
EACH item OUTER-JOIN
WHERE movto-estoq.it-codigo = item.it-codigo,
EACH item-cli OUTER-JOIN
WHERE item-cli.item-do-cli BEGINS pc-item-cli
AND movto-estoq.cod-emitente = item-cli.cod-emitente
AND movto-estoq.it-codigo = item-cli.it-codigo
AND movto-estoq.un = item-cli.unid-med-cli,
EACH nota-fiscal OUTER-JOIN
WHERE movto-estoq.nro-docto = nota-fiscal.nr-nota-fis
BY movto-estoq.dt-trans DESCENDING BY movto-estoq.hr-trans DESCENDING.
The problem that is happening is when 1 element in null, all the other elements that are in the OUTER-JOIN are appearing as null as well, even though they are not null. Is there a better way to write this code? Should I put 'LEFT' before the OUTER-JOIN? Thanks for your time.
To make your example easier, consider making it work from ABL dojo. The following code:
define temp-table ttone
field ii as int
.
define temp-table tttwo
field ii as int
field cc as char
.
create ttone. ttone.ii = 1.
create ttone. ttone.ii = 2.
create tttwo. tttwo.ii = 2. tttwo.cc = "inner".
create tttwo. tttwo.ii = 3. tttwo.cc = "orphan".
define query q for ttone, tttwo.
open query q
for each ttone,
each tttwo outer-join where tttwo.ii = ttone.ii.
get first q.
do while available ttone:
message ttone.ii tttwo.cc.
get next q.
end.
Can be run from https://abldojo.services.progress.com/?shareId=600f40919585066c219797ed
As you can see, this results in :
1 ?
2 inner
The join which is not available is shown as unknown. The value of the outer part of the join is shown.
Since you do not show how you are getting an unknown value for everything, maybe you are concatenating the unknown values?

SQLite SUM Problem, it does not sum correctly?

Hello there and good morning!
I have a litte problem with the Database of the Time Tracking Software of our company.
The first problem: the time is in a dumb format. If someone works 7h 30m, the database writes 7,30 as value. So far so good. So I have to split the decimals, convert it to industrial time, and put it back together. So far so good. It works, if the value is under 10. Above 10, the value converts itself into INT. But that case should be intercepted by my code.
Here's the code:
SELECT PersNr, Name, CASE WHEN substr(IstStd,3,1) LIKE ',' OR '.' THEN (SUM(CASE WHEN (substr(IstStd,4,2)/60 NOT LIKE 0) THEN ROUND((substr(IstStd,4,2))/60 + (substr(IstStd,1,2)),2) ELSE (IstStd) END)) ELSE (SUM(CASE WHEN (substr(IstStd,3,2)/60 NOT LIKE 0) THEN ROUND((substr(IstStd,3,2))/60 + (substr(IstStd,1,1)),2) ELSE (IstStd) END)) END AS IstStd
FROM ARCHIV_JOURNAL WHERE PersNr ='3041' AND Datum BETWEEN '2019-10-01' AND '2019-10-31'
As you can see in the first CASE, I check if the third character is , or . . The Code works fine, just not for 10h+.
Did I miss something? I'd appreciate any help with that.
If you need more information, just hit me back.
Thank you in advance and have a nice day!
The condition:
WHEN substr(IstStd,3,1) LIKE ',' OR '.'
is not correct.
It is interpreted as:
WHEN (substr(IstStd,3,1) LIKE ',') OR ('.')
If you want to check if the 3d char is ',' or '.' you must do:
WHEN substr(IstStd,3,1) IN (',', '.')
Also in other parts of your code you use the operator LIKE when you should use = or <>.
Change to this:
SELECT PersNr, Name,
CASE
WHEN substr(IstStd,3,1) IN (',', '.')
THEN SUM(
CASE
WHEN (substr(IstStd,4,2)/60 <> 0) THEN ROUND((substr(IstStd,4,2))/60 + (substr(IstStd,1,2)),2)
ELSE (IstStd)
END
)
ELSE SUM(
CASE
WHEN (substr(IstStd,3,2)/60 <> 0) THEN ROUND((substr(IstStd,3,2))/60 + (substr(IstStd,1,1)),2)
ELSE (IstStd)
END
)
END AS IstStd
FROM ARCHIV_JOURNAL
WHERE PersNr ='3041' AND Datum BETWEEN '2019-10-01' AND '2019-10-31'
Also when you divide integers the result is also an integer.
If you want the result of the division:
substr(IstStd,3,2))/60
to be a decimal number, then change it to:
substr(IstStd,3,2))/60.0

Using Multiple Variables to Reference a Sub-Sub-Sub Field in a Lua Dictionary

I'm new to Lua (like, yesterday new), so please bear with me...
I apologize for the convoluted nature of this question, but I had no better idea of how to demonstrate what I'm trying to do:
I have a Lua table being used as a dictionary. The tuples(?) are not numerically indexed, but use mostly string indices. Many of the indices actually relate to sub-tables that contain more detailed information, and some of the indices in those tables relate to still more tables - some of them three or four "levels" deep.
I need to make a function that can search for a specific item description from several "levels" into the dictionary's structure, without knowing ahead of time which keys/sub-keys/sub-sub-keys led me to it. I have tried to do this using variables and for loops, but have run into a problem where two keys in a row are being dynamically tested using these variables.
In the example below, I'm trying to get at the value:
myWarehouselist.Warehouse_North.departments.department_one["rjXO./SS"].item_description
But since I don't know ahead of time that I'm looking in "Warehouse_North", or in "department_one", I run through these alternatives using variables, searching for the specific Item ID "rjXO./SS", and so the reference to that value ends up looking like this:
myWarehouseList[warehouse_key].departments[department_key][myItemID]...?
Basically, the problem I'm having is when I need to put two variables back-to-back in the reference chain of a value being stored at level N of a dictionary. I can't seem to write it out as [x][y], or as [x[y]], or as [x.y] or as [x].[y]... I understand that in Lua, x.y is not the same as x[y] (the former directly references a key by string index "y", while the latter uses the value being stored in variable "y", which could be anything.)
I've tried many different ways and only gotten errors.
What's interesting is that if I use the exact same approach, but add an additional "level" to the dictionary with a constant value, such as ["items"] (under each specific department), it allows me to reference the value without issue, and my script runs fine...
myWarehouseList[warehouse_key].departments[department_key].items[item_key].item_description
Is this how Lua syntax is supposed to look? I've changed the table structure to include that extra layer of "items" under each department, but it seems redundant and unnecessary. Is there a syntactical change that I can make to allow me to use two variables back-to-back in a Lua table value reference chain?
Thanks in advance for any help!
myWarehouseList = {
["Warehouse_North"] = {
["description"] = "The northern warehouse"
,["departments"] = {
["department_one"] = {
["rjXO./SS"] = {
["item_description"] = "A description of item 'rjXO./SS'"
}
}
}
}
,["Warehouse_South"] = {
["description"] = "The southern warehouse"
,["departments"] = {
["department_one"] = {
["rjXO./SX"] = {
["item_description"] = "A description of item 'rjXO./SX'"
}
}
}
}
}
function get_item_description(item_id)
myItemID = item_id
for warehouse_key, warehouse_value in pairs(myWarehouseList) do
for department_key, department_value in pairs(myWarehouseList[warehouse_key].departments) do
for item_key, item_value in pairs(myWarehouseList[warehouse_key].departments[department_key]) do
if item_key == myItemID
then
print(myWarehouseList[warehouse_key].departments[department_key]...?)
-- [department_key[item_key]].item_description?
-- If I had another level above "department_X", with a constant key, I could do it like this:
-- print(
-- "\n\t" .. "Item ID " .. item_key .. " was found in warehouse '" .. warehouse_key .. "'" ..
-- "\n\t" .. "In the department: '" .. dapartment_key .. "'" ..
-- "\n\t" .. "With the description: '" .. myWarehouseList[warehouse_key].departments[department_key].items[item_key].item_description .. "'")
-- but without that extra, constant "level", I can't figure it out :)
else
end
end
end
end
end
If you make full use of your looping variables, you don't need those long index chains. You appear to be relying only on the key variables, but it's actually the value variables that have most of the information you need:
function get_item_description(item_id)
for warehouse_key, warehouse_value in pairs(myWarehouseList) do
for department_key, department_value in pairs(warehouse_value.departments) do
for item_key, item_value in pairs(department_value) do
if item_key == item_id then
print(warehouse_key, department_key, item_value.item_description)
end
end
end
end
end
get_item_description'rjXO./SS'
get_item_description'rjXO./SX'

delete an element from an array in classic ASP

Given the following array as an example...
arr(0)(0) = 3
arr(0)(1) = name
arr(0)(2) = address
arr(1)(0) = 7
arr(1)(1) = name
arr(1)(2) = address
arr(2)(0) = 14
arr(2)(1) = name
arr(2)(2) = address
I need to delete the middle element (id=7) from the array. I understand that I need to loop through the array and move each record that isnt to be deleted into a new array. I tried like this...
Dim newArr,i
Redim newArr(Ubound(arr))
For i = 0 to Ubound(arr)
If (CStr(arr(i)(0)) <> 7 ) Then
newArr(i) = arr(i)
End if
Next
When debugging this I can see the if statement work so I know only 2 elements are copied but newArr is empty at the end of this. What am I missing. I am a PHP coder that is new to classic asp and Im used to having array functions that make this kind of thing unnecessary. Any help appreciated. Thank you.
You don't need new array, you can just reassign the items and "crop" the array:
Const removalIndex = 1
For x=removalIndex To UBound(arr)-1
arr(x) = arr(x + 1)
Next
ReDim Preserve arr(UBound(arr) - 1)
This code will remove the array item at index 1 from the main array. If you don't know in advance the index of the item to remove, you can easily find it with a simple loop over the array.
Instead of using array you can give Scripting.Dictionary a try.
It is much more flexible, and has, among others Remove method.
I suggest using Scripting.Dictionary and using it as a List/collection instead, as it allows for insertions and deletions. See here: Lists in VBScript
I don't know the definitive answer, but if I were to take a stab in the dark guess I'd suggest that since the array is two dimensional maybe you have to explicitly refer to it that way?
Dim newArr,i
Redim newArr(Ubound(arr),3)
For i = 0 to Ubound(arr)
If (CStr(arr(i)(0)) <> 7 ) Then
newArr(i)(0) = arr(i)(0)
newArr(i)(1) = arr(i)(1)
newArr(i)(2) = arr(i)(2)
End if
Next
I see some VBScript syntax issues. First:
arr(0)(0) = 3 'ERROR: Subscript out of range
arr(0, 0) = 3 'CORRECT
Next:
ReDim newArr(Ubound(arr)) 'this is 1 dimensional array
newArr(0) = arr(0) 'this will NOT work
newArr(0) = arr(0, 0) 'this will work
And finally: why you convert to String and then compare it to an Integer with:
(CStr(arr(i)(0)) <> 7)

Resources