Related
i have to make a database access fast which contains large data of unique index mstr_nbr how can i make it fast.
in which get first mstr taking 0ms
and get next mstr takes most of the time 0ms but sometimes takes 1ms
means in 180000 for each mstr runs 12000 runs takes 1 ms which is increasing the time and if no of serial loop increases every time 12000ms second also increase this is a webspeed generated webpage how can i make it fast...anyone help
DEFINE QUERY Mstr FOR mstr scrolling.
OPEN QUERY Mstr FOR EACH mstr no-lock
where (Mstr_status = "close" or Mstr_status = "closed").
FOR EACH serial
WHERE (serial_pallet = f_pallet AND serial_f_chr11 <> "BOX")
or (serial_key begins f_pallet)
NO-LOCK BREAK BY serial_pallet by serial_parent by serial__chr11 QUERY-TUNING(LOOKAHEAD CACHE-SIZE 32768 DEBUG EXTENDED):
GET FIRST Mstr.
define variable roID as rowid no-undo.
roID = rowid(mstr).
DO WHILE NOT QUERY-OFF-END('Mstr'):
for each det fields(detnbr detmodel detlot detqty) no-lock
where (detnbr = mstr_nbr) and (detmodel = serial_parent and detlot = serial__chr11):
tinspected = tinspected + detqty.
end. /* for each */
GET NEXT Mstr.
END.
reposition mstr to rowid roID.
end.
index for mstr table
index-name field-name
badgenew 1 badgenew Active
datenew 1 datenew Active
nbridx 1 nbr Unique Primary Active
pallet 1 pallet Active
proddesc 1 proddesc Active
prodline 1 prodline Active
status 1 status Active
type 1 type Active
indexes for table serial:
actual_prod_day 1 dte04 2 serial_chr01 Active
actual_prod_line 1 serial_pallet 2 serial_dte04 3 serial_chr01 4 serial_line Active
pallet_prod 1 serial_pallet 2 serial_dte04 Active
pallet_prod_line 1 serial_pallet 2 serial_dte04 3 serial_line Active
serial_chr01 1 serial_chr01 Active
serial_chr05 1 serial_chr05 Active
serial_chr06 1 serial_chr06 Active
serial_chr11 1 serial_chr11 Active
serial_chr14 1 serial_chr14 Active
serial_dte04 1 serial_dte04 Active
serial_int01 1 serial_int01 Active
serial_line 1 serial_line Active
serial_pallet 1 serial_pallet Active
serial_parent 1 serial_parent Active
serial_serial__key 1 serial_serial__key 2 serial_parent Unique Primary Active
serial_pallet serial_key and serial_c11 all are character data type
indexes for table det:
detidx 1 detnbr 2 detpallet 3 detprodline 4 detbox 5 detlot 6 detshift Unique Primary Active
detlot 1 detlot Active
detmodel 1 detmodel Active
detnbr 1 detnbr Active
detpallet 1 detpallet Active
detprodline 1 detprodline Active
Given what we know this is how I would code it:
define temp-table tt_mstr
field mstr_nbr as integer /* or whatever the proper data type is */
index mstr_nbr-idx is primary unique /* I am assuming mstr_nbr is unique */
.
for each mstr no-lock
where mstr.mstr_status = "close"
or mstr.mstr_status = "closed":
create tt_mstr.
tt_mstr.mstr_nbr = mstr.mstr_nbr.
end.
for each serial no-lock
where ( serial_pallet = f_pallet and serial_f_chr11 <> "box" ) /* <> "box" is going to perform poorly, there may be better ways to do this */
or ( serial_key begins f_pallet ):
/* break by serial_pallet by serial_parent by serial__chr11: ** this sort of pointless, you're just adding up "tinspected", the order and the break groups have no impact */
for each det fields( detnbr detmodel detlot detqty ) no-lock
where detmodel = serial_parent and detlot = serial__chr11:
find tt_mstr where tt_mstr.mstr_nbr = detnbr no-error.
if available tt_mstr then
tinspected = tinspected + detqty.
end.
end.
Using a temp-table avoids refetching all of the "close" and "closed" records with every iteration of the "serial" table.
Maybe there is some context missing but the scrolling query and repositioning of the row seem pointless.
The selection of "serial" records does not look very efficient but I would need to know what indexes are available and what the data in serial_pallet serial_key and serial_c11 looks like. If there are just a few discrete values there may be better ways to write that.
You have single component indexes on each of the fields serial_pallet, serial_f_chr11, and serial_key. So there is not much help there.
This is speculation but if the number of discrete values in serial_f_chr11 is small you would probably be better off with a series of equality matches and OR. Suppose the valid values are BOX, JAR, BAG, and LOOSE. In that case, instead of:
where ( serial_pallet = f_pallet and serial_f_chr11 <> "box" )
or ( serial_key begins f_pallet )
you could write:
where ( serial_pallet = f_pallet and serial_f_chr11 = "jar" )
or ( serial_pallet = f_pallet and serial_f_chr11 = "bag" )
or ( serial_pallet = f_pallet and serial_f_chr11 = "loose" )
or ( serial_key begins f_pallet )
That would be even better if you have a composite index on serial_pallet + serial_f_chr11.
If the number of discrete valid serial_f_chr11 values is larger or if new values might get added then it would be better to add them to a temp-table and join on that instead.
Another option is that rather than loop over all of the master records to find matching details, select details that match the serials first. Then find the appropriate master record. That eliminates a whole layer of looping. But it depends on proper indexes in the "det" table. You must have an index that has serial_parent and detlot as leading components.
If mstr_nbr is not the same field as "nbr" (as shown in your index listing) then you need to build the TT and add an index on mstr_nbr. If, however, nbr is actually the same as mstr_nbr then you could skip the TT and directly query the database table efficiently.
I'm a beginner to programming and to Progress, first post on StackOverflow, hope I'm posting in the right place!
I have a fillin field where I enter a time (hh:mm), character format. I also have two arrows, one pointing forward and one backward, and I want them to add / subtract 20 min respectively when pushed.
What would be a good way to write code for this? Turn the current time value to integer and seconds past midnight and then add /subtract 1200 sec? How would I get the result back to a hh:mm format to display in the fillin?
Any help greatly appreciated!
/Ellen
I think this will do what you need. Have the ON CHOOSE triggers on your arrow buttons run the changeMins procedure. Pass in the character time string from your fill-in and either "Add" or "Subtract". The output value will be the new adjusted time string. You can then set the screen value of your fill-in to that output value.
DEFINE VARIABLE cTime AS CHARACTER NO-UNDO.
cTime = "12:45".
RUN changeMins (INPUT-OUTPUT cTime, INPUT "Add").
MESSAGE cTime VIEW-AS ALERT-BOX INFORMATION BUTTONS OK.
PROCEDURE changeMins:
DEFINE INPUT-OUTPUT PARAMETER pcTime AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER pcAction AS CHARACTER NO-UNDO.
DEFINE VARIABLE iHr AS INTEGER NO-UNDO.
DEFINE VARIABLE iMn AS INTEGER NO-UNDO.
/* Split the time string into hours and minutes */
ASSIGN
iHr = INTEGER(ENTRY(1, pcTime, ":"))
iMn = INTEGER(ENTRY(2, pcTime, ":"))
NO-ERROR.
IF ERROR-STATUS:ERROR THEN RETURN.
/* Adjust the time */
CASE pcAction:
WHEN "Add" THEN iMn = iMn + 20.
WHEN "Subtract" THEN iMn = iMn - 20.
END CASE.
/* Correct for boundaries */
IF iMn > 59 THEN
ASSIGN
iMn = iMn - 60
iHr = iHr + 1.
IF iMn < 0 THEN
ASSIGN
iMn = iMn + 60
iHr = iHr - 1.
IF iHr > 23 THEN iHr = iHr - 24.
IF iHr < 0 THEN iHr = iHr + 24.
/* Build the new time string */
pcTime = STRING(iHr, "99") + ":" + STRING(iMn, "99").
END PROCEDURE.
In this example, change the cTime string to different times and run it.
The Progress TIME function returns the integer number of seconds past midnight. So your idea of converting to such an integer is consistent with other uses within the 4gl which is a positive.
Personally I would keep the UI and the internal storage independent. So I would probably have a variable for the hour, another for the minute and a 3rd for seconds (if you need that). And I would use integers, rather than characters, for all 3.
I've no idea what version of Progress or what the environment that you are running in is but this quick and dirty little snippet might have some useful tidbits:
define variable hh as integer no-undo format ">9".
define variable mm as integer no-undo format "99".
define variable ss as integer no-undo format "99".
define variable myTime as integer no-undo.
form
hh mm ss
with
frame a
.
on value-changed of hh in frame a do:
if integer( self:screen-value ) > 23 then
do:
hh = 23.
display hh with frame a.
end.
end.
on value-changed of mm in frame a do:
if integer( self:screen-value ) > 59 then
do:
mm = 59.
display mm with frame a.
end.
end.
on value-changed of ss in frame a do:
if integer( self:screen-value ) > 59 then
do:
ss = 59.
display ss with frame a.
end.
end.
update hh mm ss with frame a.
myTime = (( hh * 3600 ) + ( mm * 60 ) + ss ).
display string( myTime, "hh:mm:ss am" ).
Deriving from TheDrooper's Code I would probably write something like the following code to fully utilize the built-in functions.
Shorter code that is still at least as easy to understand is often preferable.
Also consider that Progress does not have an optimizing compiler. If you can replace replace several simple statements with less statements (even if those are more complex and powerful than needed) the code is not only more maintainable but also faster.
DEFINE VARIABLE cTime AS CHARACTER NO-UNDO.
cTime = "12:45".
RUN changeMins (INPUT-OUTPUT cTime, INPUT "Add").
MESSAGE cTime VIEW-AS ALERT-BOX INFORMATION BUTTONS OK.
PROCEDURE changeMins:
DEFINE INPUT-OUTPUT PARAMETER pcTime AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER pcAction AS CHARACTER NO-UNDO.
DEFINE VARIABLE iMn AS INTEGER NO-UNDO.
iMn = INTEGER(ENTRY(1, pcTime, ":")) * 60
+ INTEGER(ENTRY(2, pcTime, ":"))
NO-ERROR. /* Calculate minutes since midnight */
IF ERROR-STATUS:ERROR THEN RETURN.
/* Adjust the time */
CASE pcAction:
WHEN "Add" THEN iMn = iMn + 20.
WHEN "Subtract" THEN iMn = iMn - 20.
END CASE.
/* Build the new time string */
pcTime = string(iMn * 60, 'HH:MM'). /* Convert minutes to seconds and convert the result to a string */
END PROCEDURE.
If you need to allow for higher hour values (eg. if the field doesn't represent a time of day but a time interval) you can't just convert the resulting integer with the string function. In that case you could write
pcTime = string(iMn / 60, '99') + ':' + string(iMn mod 60, '99').
(Both TheDrooper and Tom Bascom seem to assume time of day.)
This query returns 1.7763568394002505e-15 when it should return 0.00:
SELECT st.id
, Sum(
CASE sa.Type
WHEN 4 THEN sa.quantity * (st.price - st.commission)
WHEN 5 THEN -sa.quantity * (st.price - st.commission)
ELSE 0.0 END
) Sales
FROM sales sa
JOIN stock st
ON sa.stockid = st.id
WHERE st.id = 1
GROUP BY st.id
http://sqlfiddle.com/#!5/cccd8/3
It's looks like a classic floating point calculation issue, but how can I fix it?
I've tried casting the various columns to REAL but it doesn't make a difference.
You can simulate the result using this query:
SELECT 26.3 - 10.52 - 15.78 AS Result
SQLite's REAL isn't suitable for currency. SQlite doesn't support SQL decimal or SQL numeric data types, so your best option is to use integer, and store values as cents.
CREATE TABLE stock (
id INTEGER,
-- Store price and commission as integers, implying that price is in cents,
-- ($3.20 is stored as 320) and commission is a two-digit percentage (0.57%
-- is stored as 57). This is how scaled integers work in general.
price integer,
commission integer,
PRIMARY KEY(id)
);
CREATE TABLE sales (
id INTEGER,
stockid INTEGER,
type INTEGER,
quantity INTEGER,
PRIMARY KEY(id)
);
insert into stock values (1, 320, 57);
insert into sales values (1, 1, 4, 10);
insert into sales values (2, 1, 5, 4);
insert into sales values (3, 1, 5, 6);
This query, from your SQLfiddle, correctly returns 0.
SELECT st.id
, Sum(
CASE sa.Type
WHEN 4 THEN sa.Quantity * (st.price - st.commission)
WHEN 5 THEN -sa.Quantity * (st.price - st.commission)
ELSE 0.0 END
) Sales
FROM sales sa
JOIN stock st
ON sa.stockid = st.id
WHERE st.id = 1
GROUP BY st.id;
id Sales
---------- ----------
1 0
Casting to a more appropriate data type (not to REAL) will hide some problems--maybe even most problems or even all of them in a particular application. But casting won't solve them, because stored values are liable to be different than the values you really want.
Mike Sherrill is correct in that you probably should use integers. But for a quick-and-dirty fix, you can wrap the Sum call in a Round(__,2) to round to the nearest cent.
I have raw finance text files that I'm importing into Access 2010 and exporting in Excel format. These files contain several 14 character length fields which represent dollar values. I'm having issues converting these fields into currency because of the 14th character. The 14th character is a number represented by a bracket or letter. It also dictates whether the unique field is a positive or negative value.
Positive numbers 0 to 9 start with open bracket { being zero, A being one, B being two,...I being nine.
Negative numbers -0 to -9 (I know, -0 is a mathematical faux pas but stay with me. I don't know how else to explain it.) start with close bracket } being -0, J being -1,K being -2,...R being -9.
Example data (all belonging to the same field/column):
0000000003422{ converted is $342.20
0000000006245} converted is -$624.50
0000000000210N converted is -$21.05
0000000011468D converted is $1,146.84
Here's the query that I'm working with. Each time I execute it, the entire field is deleted though. I would prefer to stick to a SQL query if possible but I'm open to all methods of resolution.
SET FIELD_1 = Format(Left([FIELD_1],12) & "." & Mid([FIELD_1],13,1) & IIf(Right([FIELD_1],1)="{",0,IIf(Right([FIELD_1],1)="A",1,IIf(Right([FIELD_1],1)="B",2,IIf(Right([FIELD_1],1)="C",3,IIf(Right([FIELD_1],1)="D",4,IIf(Right([FIELD_1],1)="E",5,IIf(Right([FIELD_1],1)="F",6,IIf(Right([FIELD_1],1)="G",7,IIf(Right([FIELD_1],1)="H",8,IIf(Right([FIELD_1],1)="I",9,"")))))))))),"$##0.00"), IIf(Right([FIELD_1],1)="}",0,IIf(Right([FIELD_1],1)="J",1,IIf(Right([FIELD_1],1)="K",2,IIf(Right([FIELD_1],1)="L",3,IIf(Right([FIELD_1],1)="M",4,IIf(Right([FIELD_1],1)="N",5,IIf(Right([FIELD_1],1)="O",6,IIf(Right([FIELD_1],1)="P",7,IIf(Right([FIELD_1],1)="Q",8,IIf(Right([FIELD_1],1)="R",9,"")))))))))),"-$##0.00")
here is a function that you can call to convert an input string like the ones in your example into a string formatted as you desire.
Private Function ConvertCurrency(strCur As String) As String
Const DIGITS = "{ABCDEFGHI}JKLMNOPQR"
Dim strAlphaDgt As String
Dim intDgt As Integer, intSign As Integer
Dim f As Integer
Dim curConverted As Currency
strAlphaDgt = Right(strCur, 1) ' Extract 1st char from right
f = InStr(DIGITS, strAlphaDgt) ' Search char in DIGITS. Its position is related to digit value
intDgt = (f - 1) Mod 10 ' Converts position into value of the digit
intSign = 1 - 2 * Int((f - 1) / 10) ' If it's in the 1st half is positive, if in the 2nd half of DIGITS it's negative
curConverted = intSign * _
CCur(Left(strCur, Len(strCur) - 1) & _
Chr(intDgt + 48)) / 100 ' Rebuild a currency value with 2 decimal digits
ConvertCurrency = Format(curConverted, _
"$#,###.00") ' Format output
End Function
If you need to have a Currency as returned value, you can change the type returned from String to Currency and return the content of curConverted variable.
Bye.
By using date object how we can calculate over lapping weeks?
For eg:
July 31(Tuesday) is end for the week number 38, However week number 38 ends Sunday i.e Aug 4.
However months are different
Any idea on this
Thanks all
In our Calendar component, we use this code to calculate the first date in a week; and I bet it could be modified to find the last Date of the Week. IT makes use of the DateUtils library
public static const DAY_OF_MONTH:String = "date";
/**
* This method gets the first date of the week which the given date is in.
*
* #param date This is the date for which we want to process.
* #param firstDayOfWeek The first day of the week, 0 (Sunday) - 6 (Saturday); 0 is the default. It will probably be used primarily for localization purposes.
*
* #return This returns a date representing the first day of the week.
*/
public static function firstDateOfWeek( date:Date, firstDayOfWeek : int = 0 ):Date {
var dayIncrement : int = dayOfWeekLocalized(date, firstDayOfWeek);
var returnDate : Date = DateUtils.dateAdd(DateUtils.DAY_OF_MONTH,-dayIncrement,date);
return returnDate;
}
/**
* This method returns the position of the day in a week, with respect to the firstDayOfWeek localization variable.
*
* If firstDayOfWeek is 0; then the week is display 0 (Sunday), 1 (Monday), 2 (Tuesday), 3 (Wednesday), 4 (Thursday), 5 (Friday), 6 (Saturday).
* So, a Sunday would return 0, a Saturday would return 6, and so on.
*
* If firstDayOfWeek is 1; then the week is displayed as 0 (Monday), 1 (Tuesday), 2 (Wednesday), 3 (Thursday), 4 (Friday), 5 (Saturday), 6 (Sunday).
* However, this situation will not change the date.day value. For display purposes we need a Sunday to return 6, a Saturday to return 5, and so on.
*
* This will presumably be used for display purposes.
*
* #param date This is the date to process.
* #param firstDayOfWeek The first day of the week, 0 (Sunday) - 6 (Saturday); 0 is the default. It will probably be used primarily for localization purposes.
*
* #return This returns a date representing the day’s location on the localized week display.
*/
public static function dayOfWeekLocalized( date:Date, firstDayOfWeek : int = 0 ):int {
var result : int = date.day - firstDayOfWeek;
if(result < 0){
result += 7;
}
return result;
}
To find the last date of a week, I suspect you can just call the firstDateOfWeek and add 6 days:
public static function lastDateOfWeek( date:Date, firstDayOfWeek : int = 0 ):Date {
var firstDateOfWeek : Date = firstDateOfWeek(date, firstDayOfWeek);
var returnDate : Date = DateUtils.dateAdd(DateUtils.DAY_OF_MONTH,6,firstDateOfWeek );
return returnDate;
}
Note: The second batch of code was written in a browser and is completely untested.
Update:
Given a specific date, you can find out the weekOfYear number using the weekOfYear method in the DateUtils library. Use the methods above to find the first and last date of the week in question
Conceptually like this:
var weekOfYear : Number = DateUtils.weekOfYear(myDate);
var firstDayOfWeek : Date = firstDateOfWeek(myDate);
var lastDayOfWeek : Date = lastDateOfWeek(myDate);
I dealt with this very question in this blog post. It's very simple, really. The last day of any given month is one day less than the first day of the next month. If the last day isn't a Saturday, you have an overlap.