SQLite SUM Problem, it does not sum correctly? - sqlite

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

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.

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

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.

Remove Leading Zero(s) For Each Section Separated by '.' Except When '.0.'

In TSQL I need to create a script to remove all leading zero's between '.', starting with the first 0 in the string EXCEPT when there is a '.0.' anywhere in the middle of the task number. What I have so far does fine when the task number does not have EX: 111.0.123.234, but I cannot seem to figure out the combination of SUBSTRING/CHARINDEX/PATINDEX, etc. to remove all leading zeros except when the zero is the only number between '.' See attached screenshot and script/partial results as an example.
Here is my script:
SELECT TASKPRODUCTID, (CASE WHEN TASKPRODUCTID NOT LIKE '%.0.%' THEN
REPLACE(LTRIM(REPLACE(TASKPRODUCTID,'0','')),' ','0') ELSE
SUBSTRING(TASKPRODUCTID, PATINDEX('%[^0 ]%', TASKPRODUCTID + ' '), LEN(TASKPRODUCTID))
END) AS NewTaskProductID
FROM TASK
Here is a partial resultset:
TASKPRODUCTID NewTaskProductID
003.007.002.001 3.7.2.1
003.007.002.003 3.7.2.3
004.003.003.008.0.2017275.132925 4.003.003.008.0.2017275.132925
004.005.001.003 4.5.1.3
004.005.004.004.0.2017275.135139 4.005.004.004.0.2017275.135139
004.005.007.005.0.2017275.140304 4.005.007.005.0.2017275.140304
002.001.002 2.1.2
002.004 2.4
016.010 16.1
Screenshot Example of leading zero in highlighted rows still have leading zeros in next decimal section(s)
Thank you!!
You could try using the STUFF() to get the .0. in the else case scenario
declare #s AS varchar(100) = '0010.0.005.006025'
print stuff(REPLACE(LTRIM(REPLACE(#s,'0','0')),'0','*'),
patindex('%.*.%',REPLACE(LTRIM(REPLACE(#s,'0','0')),'0','*')), 3,'.-.')
OUTPUT
**1*.-.**5.**6*25
Now treat this as first scenario ; only difference is that we have got '*' instead of '0'

query where at least one of two fields falls into date range (

I've got a query that runs on a view that contains two modifiedBy dates. I need to return all records where either of these dates falls into a specified range.
From everything I've researched it seems I need something like this:
qbdsCustTableAddressView
.addRange(fieldNum(TCMCustTableAddressView, CustTableModified))
.value(
strFmt("(%1>='%2' AND %1<='%3') || (%4>='%2' AND %4<='%3')",
fieldstr(TCMCustTableAddressView, CustTableModified),
DateTimeUtil::toStr(contract.parmFromDateTime()),
DateTimeUtil::toStr(contract.parmToDateTime()),
fieldstr(TCMCustTableAddressView, EBillModified),
0
)
);
when I compare the resulting query to what is produced by:
qbdsCustTableAddressView
.addRange(fieldNum(TCMCustTableAddressView, CustTableModified))
.value(strFmt("%1..%2", contract.parmFromDateTime(), contract.parmtoDateTime()));
Then the above looks correct by I'm getting a non-specific "Syntax error near 22"
You have a few issues with the parenthesis, single quotation marks and using AND instead of &&.
This should work:
qbdsCustTableAddressView
.addRange(fieldNum(TCMCustTableAddressView, CustTableModified))
.value(
strFmt("(((%1 >= %2) && (%1 <= %3)) || ((%4 >= %2) && (%4 <= %3)))",
fieldstr(TCMCustTableAddressView, CustTableModified),
DateTimeUtil::toStr(contract.parmFromDateTime()),
DateTimeUtil::toStr(contract.parmToDateTime()),
fieldstr(TCMCustTableAddressView, EBillModified),
0
)
);
Try this:
qbdsCustTableAddressView
.addRange(fieldNum(TCMCustTableAddressView, CustTableModified))
.value(SysQuery::range(contract.parmFromDateTime(), contract.parmtoDateTime()));
The difference being SysQuery::range(<from>, <to>)
I don't see an obvious problem, but perhaps that might flush it out for you.

Format zero currency value with {0:C} in VB.Net

I am trying to format a zero currency value as an empty string, so that when the currency value is 0.00 then an empty string gets displayed rather than $0.00.
This code is part of an ASP.Net app that will display currency value to end user.
I have used following code to achieve this goal.
Question : Is it possible to achieve this by just using {0:C} format string or another version of this format string instead of using if then else coding for this? If I use ###,###,###.## as the data format string then an empty string shows for zero currency value and also I get rid of the if then else coding but for non-zero values no currency symbol shows.
If Double.Parse(Decimal.Parse(CDec(currencyValue))) = 0 Then
charValue = Nothing
Else
charValue = String.Format("{0:C}", CDec(currencyValue))
End If
UPDATE
I ended up using the following code, which is working fine. If is better than IIf because it does short-circuiting, which means that IIf will evaluate all expressions whether the condition is true or false but If will evaluate the first expression only if condition is true and evaluate the second expression only if condition is false.
Dim d As Decimal
Decimal.TryParse(currencyValue, d)
charValue = If(d = 0D, Nothing, String.Format("{0:C}", d))
I don't think there is a way using formatting to display an empty string.
But you can write it like:
charValue = If( currencyValue = 0D, "", currencyValue.ToString("C") )
using the If Operator (Visual Basic).
Also this is something I would not do:
If Double.Parse(Decimal.Parse(CDec(currencyValue))) = 0 Then
If currencyValue is Decimal:
If (currencyValue = 0D) Then
If currencyValue is Double:
If (currencyValue = 0R) Then
Also, if you are using a database and this is a Sql Server mind SQL Server Data Type Mappings
I don't think you can when using C or the other similar standard formats, since they are already defining a culture-specific format that will include a format for zero.
But if you specify your own custom format, you can specify three different formats separated by ;s, one each for positive numbers, negative numbers, and zero, respectively.
For example (giving an empty string for the zero format, resulting in blank zeroes):
charValue = String.Format("{0:#,##0.00;-#,##0.00;""""}", CDec(currencyValue))
And from what I can see, omitting the format for negative gives a default that matches the positive, whereas omitting the format for zero gives blank, which is what you're looking for, so this should be sufficient as well:
charValue = String.Format("{0:#,##0.00;;}", CDec(currencyValue))
(Using whichever custom format you wish.)
UPDATE: You can get the current currency symbol and manually put it into your custom format. IE:
Dim symbol = CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol
charValue = String.Format("{0}{1:#,##0.00;;}", symbol, CDec(currencyValue))
From the sound of it, though, I think I would actually recommend doing basically what you started with, maybe with an extension method.
<Extension>
Public Function ToCurrencyString(pValue As Decimal) As String
Return IIf(pValue = 0, "", pValue.ToString("C"))
End Function
Dim someValue As Decimal = 1.23
Console.WriteLine(someValue.ToCurrencyString())
This gives you exactly what you're looking for. The exact same format as C gives, but with blank zeroes.

Resources