VBA Macro to subtract dates by duplicates ID - ms-access

I would like to create a Macro in VBA to identify the first date and the last date by ID and then get the subtract result between them formatted in hours.
This is an example of my Table:
ID DATE
001 11/11/2013
001 11/21/2013
001 11/25/2013
002 12/04/2013
002 12/05/2013
003 12/05/2013
001 11/23/2013
The desired result:
ID DATE RESULT IN HOURS (Last Date - First Date)
001 11/11/2013 =(11/25/2013)-(11/11/2013)
001 11/21/2013
001 11/25/2013
002 12/04/2013 =(12/05/2013)-(12/04/2013)
002 12/05/2013
003 12/05/2013
001 11/23/2005
In the current table, there are 2 or more duplicates IDs with different Dates as you can see in the 001 ID Sample.
My first solution was sorting the table by ID and Date and then apply a Countif formula to get the duplicates IDs but I was only able to identify the First Date by ID and missing the Last Date.
I would very much appreciate your help. Thanks!

Nice problem. I like the use of collections, because they are quite fast. There must be numerous ways solve a problem like this, the following is one example:
Sub jzz()
Dim cArray() As Variant
Dim lastRow As Long
Dim rw As Long
Dim i As Long
Dim firstDate As Date
Dim lastDate As Date
Dim id As Long
Dim idColl As Collection
Set idColl = New Collection
'determine last row:
lastRow = Cells.SpecialCells(xlLastCell).Row
'if you plan to loop the cells repeatedly,
'the use of an array to hold cell values pays off, performancewise
cArray = Range(Cells(1, 1), Cells(lastRow, 2)).Value
For rw = 1 To lastRow
id = Cells(rw, 1)
'determine if id is used or not:
For i = 1 To idColl.Count
If idColl(i) = id Then GoTo nextRow
Next i
'id is not in collection, add it:
idColl.Add id
firstDate = cArray(rw, 2)
lastDate = 0
'loop array to find first and last dates:
For i = LBound(cArray) To UBound(cArray)
If cArray(i, 1) = id Then
If cArray(i, 2) < firstDate Then firstDate = cArray(i, 2)
If cArray(i, 2) > lastDate Then lastDate = cArray(i, 2)
End If
Next i
Debug.Print id, firstDate, lastDate
nextRow:
Next rw
End Sub
Private Function TimeDiffHours(T0 As Date, T1 As Date) As String
'function that returns the difference between T0 and T1 in hours:mins
TimeDiffHours = Int((T1 - T0) * 24) & ":" _
& Format(Round(((T1 - T0) * 24 - Int((T1 - T0) * 24)) * 60, 0), "00")
End Function
Note that this only finds the first and the last dates for each id and prints it out, along with the time difference in Hours:mins. It does nothing with it, not even save results. That is up to you now.

Related

How to add new Access record by checkbox and number of different date?

I have query 1 or table 1 have the below data
Number of employee/start date/end date/code/different date by Days/checkbox
Example
200/01-01-2021/15-01-2021/E/14/Yes
I need when checkbox=Yes to open 14 record automatically in new table 2 with code like below:
employee/date/code
200/01-01-2021/E
200/02-01-2021/E
200/03-01-2021/E
200/04-01-2021/E
200/05-01-2021/E
200/06-01-2021/E
200/07-01-2021/E
200/08-01-2021/E
200/09-01-2021/E
200/10-01-2021/E
200/11-01-2021/E
200/12-01-2021/E
200/13-01-2021/E
200/14-01-2021/E
Extract your parameters:
Data = "200/01-01-2021/15-01-2021/E/14/Yes"
ENo = Split(Data, "/")(0)
FirstDate = DateValue(Split(Data, "/")(1))
ECode = Split(Data, "/")(3)
Periods = Split(Data, "/")(4)
Then run this query passing it the parameters:
PARAMETERS
Periods Short,
FirstDate DateTime;
SELECT DISTINCT
10 * Abs([Deca].[id] Mod 10) + Abs([Uno].[id] Mod 10) + 1 AS Sequence,
ENo As Employee,
DateAdd("d", [Sequence] - 1, [FirstDate]) AS DateStart,
ECode As Code
FROM
MSysObjects AS Uno,
MSysObjects AS Deca
WHERE
(10 * Abs([Deca].[id] Mod 10) + Abs([Uno].[id] Mod 10)) < [Periods];
Output:
You can the use this query as source in an append query.
Or, alternatively, use DAO to directly append the records:
Dim Records As DAO.Recordset
Dim Index As Integer
Set Records = CurrentDb.OpenRecordset("Select * From Table2")
For Index = 0 To Periods - 1
Records.AddNew
Records!Employee.Value = Eno
Records!Date.Value = DateAdd("d", Index, FirstDate)
Records!Code.Value = ECode
Records.Update
Next
Records.Close

ACCESS sql code to multiply record with calculated value from previous cell

I am using Access Database to get a value. I am fairly new to access as I usually use SQLServer and I am having trouble in getting what I want.
I have the following table, with column TARGET and incremental Target as the target column that I need to get:
Category|Period|Value| TARGET |
A | 4 | 1 | 1/1 =1 |
A | 3 | 3 | 1/(3*1)=0.33 | (1/value at period 3 * previous target)
A | 2 | 6 |1/(0.33*6)=0.505|
A | 1 | 9 |1/(0.505*9)=0.22|
The data is partitioned by Category and ordered in descending order by Period.
For the first row the Target should be: (1/value at current period)
For the next rows the Target should be: (1/value at current period * value of previous target)
As you can see this is somehow complex as I need to evaluate a cell value and then for the next row I need to use the value in the cell above.
Plus I need to get the incremental value for this column as well.
Any help will be very much appreciated as I am new to Access and need to get this done soon!
Here is a function placed in general module that can be called from query. Value is a reserved word so I used Data for that field name.
Option Compare Database
Option Explicit
Global dblTar As Double
Global strCat As String
____________
Function CalcTarget(strC As String, dblT As Double) As Double
If strCat <> strC Then
strCat = strC
dblTar = dblT
End If
dblTar = 1 / (dblT * dblTar)
CalcTarget = dblTar
End Function
Calling function in query:
SELECT Category, Period, Data, CalcTarget([Category],[Data]) AS Target FROM Table1;
Normally I advise not to save calculated data to table when a query can work, but if you prefer to save, then options are:
An UPDATE action: UPDATE Table1 SET Target = CalcTarget([Category],[Data]);
Or VBA:
Sub CalcTarget()
Dim rs As DAO.Recordset
Dim strCat As String
Dim dblTar As Double
Set rs = CurrentDb.OpenRecordset("SELECT * FROM table1 ORDER BY Category, Period DESC")
strCat = rs!Category
dblTar = rs!Data
Do While Not rs.EOF
If rs!Category = strCat Then
dblTar = 1 / (rs!Data * dblTar)
rs.Edit
rs!Target = dblTar
rs.Update
rs.MoveNext
Else
strCat = rs!Category
dblTar = rs!Data
End If
Loop
End Sub

VB.NET Do Until Loop to get the Month Name

I want to get the month name from a Do Until......Loop and display in a CheckedListBox.
I have two tables.
1. Month_Count
2. Fees_Ledger
My month name function...
Function mnthName(ByVal mnth As Integer)
Dim name As String = String.Empty
name = MonthName(mnth, False)
Return name
End Function
To get the Month Details
"SELECT FromMonth,ToMonth,MonthCount FROM Month_Count WHERE SemesterNumber='1'"
And to get the Paid Count
"SELECT COUNT(*) AS TotMonthPaidCount FROM Fees_Ledger WHERE SemYear='1' AND FeeId='1'"
So...
' Got the values from queries
FromMonth = 7 '(July)
ToMonth = 6 '(June)
MonthCount = 12 '(Loop will rotate 12 times)
TotMonthPaidCount = 1 '(FeeId 1 paid one time)
'Declaring an integer variable
Dim StartM As Integer = 1
FromMonth = FromMonth + TotMonthPaidCount
' For the first month
CheckedListBoxMonth.Items.Clear()
CheckedListBoxMonth.Items.Add(mnthName(FromMonth))
' Now the loop to achieve the goal
Do Until StartM = (MonthCount - TotMonthPaidCount)
If FromMonth >= 12 Then
FromMonth = 1
CheckedListBoxMonth.Items.Add(mnthName(FromMonth))
Else
FromMonth += 1
CheckedListBoxMonth.Items.Add(mnthName(FromMonth))
End If
StartM += 1
Loop
This Subroutine results exactly what I want.
But the problem occurs when the StartMonth = FromMonth (6) + TotMonthPaidCount (7) value >12. As 13 or 14 or 15 has no Month Name, it is showing error.
Argument 'Month' is not a valid value.
I want it like below.
What should I do ?

Inserting a cell in excel based on cell value

I am working on exporting CSVs of large groups from an active directory environment. Many of these groups have extensive nesting and I need to insert cells so that the worksheet is human readable.
For example my worksheet looks like this:
WS Example
Int User Path
0 User1 CN
0 User2 CN
1 User3 CN
1 User4 CN
0 User5 CN
1 User6 CN
2 User7 CN
I am looking for help adapting a VBA script that reads the integer value from the first column and inserts the corresponding number of cells to the left of the column for that particular row. The constraint is that the list cannot change the order of the rows so as to preserve the nested structure.
Here is what I have in VBA so far
Sub test()
Dim d As Integer
d = Range("A:A").End(xlDown).row
Dim c As Range
For i = d To 1 Step -1
If Cells(i, 1).Value Like "1" Then
Rows(Cells(i, 1).Column).Insert shift:=xlShiftRight
End If
Next
End Sub
Currently this snippet counts the number of 1's from the column and inserts a new row at the top of the list. I believe the error in my logic is within the If statement and once I have that ironed out I know I can expand that with an ElseIf to address the rest of the values.
This will do it. The issue looks like it's within the line Rows(Cells(i, 1).Column).Insert shift:=xlShiftRight. If you break that down, it computes as follows:
Cells(i,1).Column which equals 1, since the column of .Cells(i,1) is 1.
Rows(1) the 1 comes from the above. So Rows(1) is 1.
Rows(1).Insert inserts above row 1, regardless of what you specify for shift.
Sub test()
Dim d As Integer
d = Range("A:A").End(xlDown).row
Dim c As Range
For i = d To 1 Step -1
If Cells(i, 1).Value Like "1" Then
Cells(i, 1).Insert shift:=xlToRight
End If
Next
End Sub
This should do it.
Sub test()
Dim d As Integer
d = Range("A:A").End(xlDown).Row
Dim c As Range
For i = d To 1 Step -1
aMove = Val(Cells(i, 1))
If aMove > 0 Then
Range(Cells(i, 1), Cells(i, aMove)).Insert shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
End If
Next
End Sub
Quite a few ways to do this, but by using the number in columnA to define the amount of cells to insert you can do a second loop like so:
Private Sub CommandButton1_Click()
Dim x, d, i As Integer
With ActiveSheet
d = .Range("A:A").End(xlDown).Row
For x = 2 To d
i = .Cells(x, 1).Value
For a = 1 To i
.Cells(x, 1).Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Next a
Next x
End With
End Sub

capture dates by quarters in access

To make my life much easier working with my data I set 3 columns of same date content. The first displays in the format mm/dd/yyyy, second in the format of yyyy-mm and the third in the format yyyy-q.
I did it purposely due to my reports. Sometimes I need to create monthly, quarterly, yearly etc. Usually I work with a form where I invite the user select start and end date and by a click of a button run a report. This report extracts a query where I specify on the date section to pull all information between start and end date. This time I want to do the same procedure but instead of start and end date - I want the user to select which quarter he wants so that the query will pull all information regarding this quarter. What do I specify in the criteria to archive this?
Filter on
DatePart("q", [YourDateField])
or
Format([YourDateField], "yyyyq")
To obtain the first and last date of a quarter, given the year and the quarter, you can use these expressions:
DateQuarterFirst = DateSerial(Y, 1 + 3 * (Q - 1), 1)
DateQuarterLast = DateSerial(Y, 1 + 3 * Q, 0)
If you have a date of the quarter, you can these functions to obtain the first and last date of the quarter of that date:
Public Function DateThisQuarterFirst( _
Optional ByVal datDateThisQuarter As Date) As Date
Const cintQuarterMonthCount As Integer = 3
Dim intThisMonth As Integer
If datDateThisQuarter = 0 Then
datDateThisQuarter = Date
End If
intThisMonth = (DatePart("q", datDateThisQuarter) - 1) * cintQuarterMonthCount
DateThisQuarterFirst = DateSerial(Year(datDateThisQuarter), intThisMonth + 1, 1)
End Function
Public Function DateThisQuarterLast( _
Optional ByVal datDateThisQuarter As Date) As Date
Const cintQuarterMonthCount As Integer = 3
Dim intThisMonth As Integer
If datDateThisQuarter = 0 Then
datDateThisQuarter = Date
End If
intThisMonth = DatePart("q", datDateThisQuarter) * cintQuarterMonthCount
DateThisQuarterLast = DateSerial(Year(datDateThisQuarter), intThisMonth + 1, 0)
End Function