How to insert every Single date into table [duplicate] - vba

hoping someone can help? I'm fairly new to Access 2016 and have been tasked with building a very simple booking system for our school's breakfast and after school clubs.
I have a table with a list of Children (primary key is ChildID), another table (CLUBS) lists the 5 clubs available, and a third table (BOOKINGS) connects the children to the clubs (ChildID, ClubID, DateRequested)
I have a simple form that enables me to select a child's name from a drop down box, then from a list choose a club, then enter the date required. This saves the record to the Bookings table.
This works fine, however to make it easier to use...I've added unbound Start and End Date fields in the form with a view to being able to quickly book a child in over a term..i.e. rather than having to add each day individually, I enter the child's name, choose a club and then enter start and end dates. Multiple records are created in the booking table with the Child ID, Club ID identical, but the DateRequested field varies.
We do need to store a record in the Bookings table for the child on each date so we can print a register for each day..as well as for invoicing/reporting.
From looking at VBA...I think I need to use the INSERT INTO command? Is the best way to do it? Also I need to make sure that dates within the range that are Sat/Sunday are ignored.
I'd really appreciate any guidance on this and pointers to which commands would work best...

This is where DAO shines. It is so much faster to run a loop adding records than calling a Insert Into multiple times.
Here is how:
Public Function PopulateBokings()
Dim rsBookings As DAO.Recordset
Dim NextDate As Date
Set rsBookings = CurrentDb.OpenRecordset("Select Top 1 * From Bookings")
NextDate = Me!StartDate.Value
While DateDiff("d", NextDate, Me!EndDate.Value) >= 0
If Weekday(NextDate, vbMonday) > 5 Then
' Skip Weekend.
Else
rsBookings.AddNew
rsBookings!ChildrenId.Value = Me!ChildrenId.Value
rsBookings!ClubsId.Value = Me!ClubId.Value
rsBookings!DateRequested.Value = NextDate
rsBookings.Update
End If
NextDate = DateAdd("d", 1, NextDate)
Wend
rsBookings.Close
Set rsBookings = Nothing
End Function
Paste the code into the code module of the form, adjust the field and control names to those of yours, and call the function from the Click event of a button.

Consider populating a separate DateRange table that holds all possible dates, like all of 2017. You can build such a table iteratively with dynamic SQL query calls in VBA. And only run this once.
Then, create a stored query that cross joins Children, Club, and DateRange with filters for all by form parameters. This returns all possible date ranges repeating same Child and Club for table append.
VBA
Public Sub PopulateTime()
Dim i As Integer, StartDate As Date
CurrentDb.Execute "CREATE TABLE DateRange (" _
& " [ID] AUTOINCREMENT PRIMARY KEY," _
& " [BookDate] DATETIME)", dbFailOnError
StartDate = DateSerial(2017, 1, 1)
For i = 0 To 364
CurrentDb.Execute "INSERT INTO DateRange ([BookDate])" _
& " VALUES (#" & DateAdd("d", i, StartDate) & "#);", dbFailOnError
Next i
End Sub
SQL
INSERT INTO Bookings (ChildID, ClubID, DateRequested)
SELECT c.ChildID, b.ClubID, d.BookDate
FROM Children c, Clubs b, DateRange d
WHERE c.ChildID = Forms!myformname!ChildID
AND b.ClubID = Forms!myformname!ClubID
AND d.BookDate BETWEEN Forms!myformname!StartDate
AND Forms!myformname!EndDate

You can use a sequence generator query to repeatedly insert rows into a table between two parameters.
For this example, the maximum number of days inserted is 999, but this can easily be increased to 9999 or even more.
Inspired by this answer by Gustav:
PARAMETERS [StartDate] DateTime, [EndDate] DateTime;
INSERT INTO MyTable(MyDateField)
SELECT DISTINCT [StartDate] - 1+ 100*Abs([Hundreds].[id] Mod 10) + 10*Abs([Tens].[id] Mod 10)+Abs([Ones].[id] Mod 10)+1
FROM MSysObjects As Ones, MSysObjects As Tens, MSysObjects As Hundreds
WHERE [StartDate] - 1+ 100*Abs([Hundreds].[id] Mod 10) + 10*Abs([Tens].[id] Mod 10)+Abs([Ones].[id] Mod 10)+1 Between [StartDate]-1 And [EndDate]
Performance won't be good, but there are multiple advantages using a non-VBA solution.

Related

Access claiming duplicate record with no unique fields updated with Update Recordset/Addnew

I'm trying to allow my client to add a bunch of dates in a row with the few fields copied. None of these fields are unique in the table but it keeps flagging them as duplicates records. When I got into the the table the new records are not added to the table despite there not being any duplicates.
This is the code (edited for silly mistakes)
Public Sub Create_Click()
Dim rsEventDayGroup As DAO.Recordset
Dim NextDate As Date
Set rsEventDayGroup = CurrentDb.OpenRecordset("Select * From tblEventDays")
NextDate = Me!Start.Value
While DateDiff("d", NextDate, Me!End.Value) >= 0
rsEventDayGroup.AddNew
rsEventDayGroup!Start.Value = NextDate
rsEventDayGroup!EventID.Value = Forms!frmEvents.Controls!ID.Value
rsEventDayGroup!DayType.Value = Me!DayType.Value
rsEventDayGroup!Period.Value = Me!Period.Value
rsEventDayGroup.Update
NextDate = NextDate + 1
Wend
rsEventDayGroup.Close
Set rsEventDayGroup = Nothing
End Sub
I'm trying to copy fields EventID, DayType, and Period and create records for each day someone inputs. tblEventDays has it's own unique ID, called just "ID" that is an autonumber. I suspect it's the code screwing up somewhere, but as it was working just yesterday and I haven't changed it, I'm not sure what it could be. When I go to debug it highlights "rsEventDayGroup.Update" I've also tried changing Select * from the table with no luck.

Selectively deleting duplicates from table based on field value in Access

I have the following table (ID column exists but not shown below) :
Email
Course
DateComplete
1#1.com
Running
01/01/2021
1#1.com
Running
1#1.com
Running
2#2.com
Walking
2#2.com
Walking
2#2.com
Walking
I'd like to know if it is possible to delete all duplicate (of Email&Course) records from my table, but also ensuring that no records with a value in DateComplete are deleted.
So after running the query I would have :
Email
Course
DateComplete
1#1.com
Running
01/01/2021
2#2.com
Walking
You just need a query with an aggregation such as
SELECT Email, Course, MAX(DateComplete) AS DateComplete
INTO [dbo].[new_table]
FROM [dbo].[current_table]
GROUP BY Email, Course
Run a loop that deletes the dupes:
Public Function DeleteDupes()
Const Sql As String = "Select * From Course Order By Email, Course, DateComplete Desc"
Dim Records As DAO.Recordset
Dim Values As String
Set Records = CurrentDb.OpenRecordset(Sql)
While Not Records.EOF
If Values <> Records!Email.Value & Records!Course.Value Then
Values = Records!Email.Value & Records!Course.Value
Else
Records.Delete
End If
Records.MoveNext
Wend
Records.Close
End Function

SQL query to display rows of 2 different tables

I'm trying to figure out how to pull all rows from two different tables with the OutageID = X. The purpose of this is to view the current outage and all the revisions in one statement to see all of the changes. Below is an example I was trying. However, it puts it all in one row. I want it to display all rows separately like you would if you were to query SELECT * From Table WHERE X = Y.
The Current Outages are in one table and the history is in another so they are not written over and not to change the design of the current DB.
Outages Table
`strSQL = "SELECT Outages.OutageID, Outages.Outage, Outages.Building,
Outages.OutageType, Outages.OutageStart, Outages.OutageStartTime,
Outages.OutageEnd, Outages.OutageEndTime, Outages.Duration,
Outages.Reason, Outages.Areas, Outages.Comment, Outages.ORN,
Outages.Contact, Outages.Phone, Outages.Job, Outages.Timestamp
FROM Outages
WHERE (((Outages.OutageID)=3305));"`
Outage History Table
`strSQL = "SELECT OutageHistory.RevisonID, OutageHistory.OutageID,
OutageHistory.Outage, OutageHistory.Building,
OutageHistory.OutageType,
OutageHistory.OutageStart, OutageHistory.OutageStartTime,
OutageHistory.OutageEnd, OutageHistory.OutageEndTime,
OutageHistory.Duration, OutageHistory.Reason, OutageHistory.Areas,
OutageHistory.Comment, OutageHistory.ORN, OutageHistory.Contact,
OutageHistory.Phone, OutageHistory.Job, OutageHistory.Timestamp
FROM OutageHistory
WHERE (((OutageHistory.OutageID)=3305));"`
`Private Sub All_Revision_Histoy_Click()
Dim strSQL As String
strSQL = "SELECT * From OutageHistory WHERE OutageHistory.OutageID = " &
Me.OutageID & ";"
Debug.Print strSQL
ShowDataSheet strSQL`
I think that I might need to create a temp table and insert both rows for the results and then Delete the table when its closed. However, I am not sure how to do that. I already feel I may of bitten off more than I can chew with this one. Thank you in advance.
select * from (
select 1 as revisionID, Outages.* FROM Outages
WHERE (((Outages.OutageID)=3305))
union
select OutageHistory.* FROM OutageHistory
WHERE (((OutageHistory.OutageID)=3305))
) order by revisionID desc

Multiple 'AND' conditions on same column

I'm using an Access 2007 database in ASP.Net where I need to extract data from a table if an item meets multiple 'AND' clauses.
For example:
Select * from tableA
where tableA.column1 = myvalue1
and (tableA.column2 = myvalue2
and tableA.column2 = myvalue3)
(The '(' and ')' parenthesis around the 'and' clauses are an Access requirement)
myvalue1 exists multiple times in column1 because column2 stores many different pieces of data for the column1 value.
However, I only want the results to show those column1 values which meet all of the column2 search criteria.
For example, column1 contains employee surnames, column2 contains the qualifications of the employee.
One employee in column1 may hold many qualifications in column2.
Searching with the 'OR' clause for employees who hold all the required qualifications for an assignment would produce a table with multiple rows for each employee.
What is required is a single row for each employee who holds every one of the required qualifications.
Let's say employee A has qualifications X, Y and Z. The employee's name will be in a row in column 1 each time a qualification is added to column2, so the employee's name appears in column1 3 times. Assume now employee B also has qualifications X, Y and Z but employee C has W, X and Y. If an assignment needs qualifications X,Y and Z I don't want employee C appearing in the results of a search as employee C won't hold the relevant qualifications.
When I use the above 'select' command with just one 'and' clause the search works fine, but with two 'and' clauses nothing is returned, even though I know there are items in column1 that meet both of the 'and' criteria.
What am I missing in the concatenation of the 'and' clauses?
The following solution will work, but performance will suffer. This will return 1 row per person, I can rewrite it to return both rows.
Select * from tableA
where tableA.column1 = myvalue1
and (tableA.column2 = myvalue2
and DCount("column2", "TableA", "column1 = """ & column1 & """ AND column2 = """ & myvalue3 & """" ) <> 0)
Your fundamental problem is that a select query without a group by or pivot clause only evaluates 1 row at a time. I'd probably pivot the data, concatenate it, and then evaluate it for better performance in the end. But that's more complicated, and requires more information on the table structure and what you want to do.
Based on new information, I'll submit this answer:
My gut feeling is that you can't do this in one simple query. This sounds cludgy, but I think you're going to need one query created that's a SELECT DISTINCT query, and that query will pull one record for each employee:
Select DISTINCT Column1 from tableA
Call this query something like qryEmployees
Next you're going to need to create a new table. Call it tblQualifications. It will contain 2 fields; EName and EQualifications. We're going to fill that in a minute.
Next you're going to need to loop through each record in qryEmployees and write the employee name and their qualifications into tblQualifications.
'Run through every Employee name
Do While qryEmployees.EOF = False
'Set up a temp variable, MyQual, and set it equal to Empty
MyQual = ""
'Pull all qualifications from TableA for the current Employee
Set rec = db.OpenRecordset ("Select Column1, column2 from TableA WHERE Column1 = '" & qryEmployees.Column1 & "'")
'Looping through the qualifications
Do While rec.EOF = False
'Add the qualification to the MyQual string
MyQual = MyQual & rec("Column2") & ", "
rec.MoveNext
Loop
'Now that we have all the qualifications in a string, trim off the last ", "
MyQual = left(MyQual, len(MyQual)-2)
'Open up tblQualifications and fill it with the Employee and a string of all of their qualifications
tblQualifications.AddNew
tblQualifications(0) = qryEmployees.Column1
tblQualifications(1) = MyQual
tblQualifications.Update
'Move on to the next Employee and repeat the process
qryEmployees.MoveNext
Loop
Now you have one table with Employee Name in one field, and all of his qualifications in the next. If you use the LIKE operator on this table, you should get your results.
Select * from tblQualifications
where tblQualifications.EName = myvalue1
and (tblQualifications.EQualifications LIKE myvalue2
and tblQualifications.EQualifications LIKE myvalue3)
It's not pretty, but it will work (with a little putzing with the code above, as I'm writing it off the top of my head and it's untested).
Thanks Johnny, your suggestion inspired me to write a slightly different approach to the solution.
I swapped the 'AND' to an 'OR' as others have suggested to extract any employee into a table if they have any of the qualifications.
The result is a table with every required qualification for every employee who has one of those qualifications.
I then did a count elsewhere of the number of qualifications required for the assignment and compared this to whether or not the employee had the required number of qualifications, as follows:
Dim objSearchTable As DataTable = DSSearch.Tables("Employees")
Dim i As Integer
If QualificationsCount > 0 Then
For i = 0 To objSearchTable.rows.count - 1
strEmployee = objSearchTable.Rows(i).Item("column1")
Dim foundRows As DataRow() = objSearchTable.[Select]("column1 = '" & strEmployee & "'")
Dim numberOfRecords As Integer = foundRows.count
If numberOfRecords < QualificationsCount Then
objSearchTable.Rows(i).Delete
End If
Next
End If
objSearchTable.AcceptChanges
This deletes every instance of an employee from the table if the count of qualifications is less than the number required.
The solution isn't as elegant as extracting only the correct employees from the database to begin with.
There is also an overhead on extracting every employee from the database, but the end result is a table with just one row for each employee with all the qualifications, which does what I need.

Removing "old" data from an Access table

I have two tables (tblCurrentStudents and tblNewStudents) in an Access file. tblCurrentStudents was current at of the end of the last school year and contains a list of last year's students and their information. tblNewStudents contains a complete list of this year's student information. There are some students common to both tables, meaning they were here last year and are here this year. However, in tblCurrentStudents there are some students who were NOT here last year, but enrolled at the beginning of this year.
What I need to do now is remove the students from tblCurrentStudents who are no longer enrolled, meaning they left at the end of last year and will not be in tblNewStudents.
I know I can join two tables together using a Union command (I found the necessary code to do this). I don't know how to do the opposite, meaning find those records not common to both tables and remove just those records from tblCurrentStudents.
Any suggestions? Is there such as thing as a Not Union command?
Assuming I'm understanding your question correctly, you want to remove any student from tblCurrentStudents who are not in tblNewStudents?
If so, I think you're looking to use IN():
DELETE FROM tblCurrentStudents
WHERE StudentId NOT IN (
SELECT StudentID FROM tblNewStudents)
I created a practice set of tables that simulate what I want. This is the UNION code I found on the Internet and used:
Private Sub Command0_Click()
Dim sql As String, sTable As String, oTD As TableDef
sql = "SELECT * FROM MergeTable UNION SELECT * FROM MainTable"
If MsgBox("Store unique records in a Table?", vbYesNo) = vbYes Then
sTable = InputBox("Which table name?", , "tblMerged")
For Each oTD In CurrentDb.TableDefs
If oTD.Name = sTable Then sTable = sTable & "1"
Next oTD
sql = "SELECT * INTO " & sTable & " FROM (" & sql & ")"
DoCmd.RunSQL sql
End If
End Sub
Assuming I'm understanding your question correctly, you want to remove
any student from tblCurrentStudents who are not in tblNewStudents?
Yep, that's exactly what I need. I'm going to try the IN command. I'll let you know how it goes.