Is there a way to append records from excel in to my main table allowing the user to still add additional fields in to the same table? I already have a search box that will populate the form with the record you search for. So, ideally, the user types search criteria for one (unique) field, then about half of the fields should auto-populate so the user can manually enter the remaining half and save the updated record.
This needs to be done multiple times per day, so im trying to find the easiest/most reliable way possible!
Also, data-validation is not good in the excel source (another reason we are building this database). So the imported data is not 100% consistent (all entered manually by people)!
You can do this via VBA. It will be a little time consuming, but if you're intending this to be a long term solution (I hope not?) then you'll need to do it that way. Here is a little snippet for you to start with:
'Open Excel and grab data
Dim oExcel As Object
Dim oBook As Object
Dim oSheet As Object
Set oExcel = CreateObject("Excel.Application")
Set oSheet = oExcel.Open("PathOfWorksheet").Worksheets("WorksheetNumber")
msgbox oSheet.cells(1, 1).Value
oExcel.Quit
Combine that with CurrentDB.Execute "INSERT INTO tblMyTable (Field1, Field2) VALUES (val1,val2)"
Related
it's 3:20 am and I'm about spent, so I'm tossing this up here in hopes someone can help me. I'm not sure this is a difficult problem, but I don't honestly know how to ask this clearly.
I made a User Form a couple of weeks ago with some help here to let users store information into a table. I'm now making a Search form to allow them to search the table (namely the full name column) and as there will be multiple entries with the same name, have it propagate a combo box so that the user can choose which entry they want to view. Depending on which entry they choose in the combobox will also propagate all the fields below it.
First, I think I've got the search function working correctly and building the array of ranges right. I had originally stored the array as strings and it populated my combo box perfectly, but then I had lost the range/address to propagate other data later. So I switched it back to an array of ranges and from there I'm having problems. Currently if I use the Combobox.additem I will of course only get a range from my array, but I can't do something like LookUpTable.Range(Array(i)).Value for my AddItem either. So, I'd like to be able to figure out how to propagate the combobox with the values in those stored ranges. I think once I learn how to do that, propagating the other fields afterwards will be pretty straightforward.
I hope this makes sense. Apologies, my brain is fried and I need some sleep.
EDIT:
The combobox will be propagated with all the duplicates as well as an identifier to easily separate them (in this case the date and person who did the evaluation) so that the user can choose which evaluation they would like to view. Right now it just shows the Full Name which is the stored range. I want to be able to essentially use the stored range to grab the entire row of values in another array that can then propagate all the fields for that report. I could make an array for every result at the time of searching, but this would be inefficient I think. Instead it should be created once the user chooses which report they want to view so it's limited to only making one array. I think I can maybe figure that out, but because it happens after they choose from the combobox, I'm unable to figure out to use that one range and pull two more columns of data with it. If I try using ,Offset with it I get an "Expected Object" error. If I try using my Table and the Array value for a range, I get a different error. I hope all this makes sense.
Public Sub Search_button_Click()
Dim NameColumn As Range
Dim NameLookUp As Range
Dim SearchResultsArray() As Variant
Dim SearchResultsCounter As Integer
Dim ResultsPropagate As Integer
Dim FirstResult As String
'Sets/Resets counter to 1 each time search button is pressed
SearchResultsCounter = 1
'Converts the text box values to strings and uppercases the first character and combines them into a full name value.
FirstLookUp = StrConv(StudentFirst_textbox.Value, vbProperCase)
LastLookUp = StrConv(StudentLast_textbox.Value, vbProperCase)
FullLookUp = FirstLookUp & " " & LastLookUp
'Sets NameColumn to the Full Name column in the table
Set NameColumn = LookUpTable.Range.Columns(3)
'Sets NameLookUp to the Full Name column in the table and searches for the FullLookUp string
Set NameLookUp = LookUpTable.Range.Columns(3).Find(What:=FullLookUp, LookIn:=xlValues, _
LookAt:=xlPart, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
'Saves the first result to prevent infinit looping and readjusts the array to match results size.
If Not NameLookUp Is Nothing Then
FirstResult = NameLookUp.Address
ReDim Preserve SearchResultsArray(SearchResultsCounter)
SearchResultsArray(SearchResultsCounter) = NameLookUp
Do
ReDim Preserve SearchResultsArray(SearchResultsCounter)
SearchResultsArray(SearchResultsCounter) = NameLookUp
Set NameLookUp = NameColumn.FindNext(NameLookUp)
SearchResultsCounter = SearchResultsCounter + 1
Loop Until NameLookUp Is Nothing Or NameLookUp.Address = FirstResult
SearchResults_combobox.AddItem ("Choose a result to view.")
For ResultsPropagate = LBound(SearchResultsArray) To UBound(SearchResultsArray)
SearchResults_combobox.AddItem (SearchResultsArray(ResultsPropagate)) 'Here I want to use the range stored in the array and pull the value from the table.
Next ResultsPropagate
SearchResults_combobox.ListIndex = 0
Else
MsgBox "Sorry, no entries matched your search.", vbOKOnly
End If
End Sub
A report I am creating in Excel involves several very similar pivot tables needing to be specifically filtered many times (i.e. a Year-to-Date table, a Quarter-to-Date table, etc, all needing to be filtered the exact same way before exported, then filtered again, then exported, etc)
So I looked into VBA as a way of accepting a few filter criteria, then filtering multiple tables that way, before looping.
However, I'm having a very tough time properly targeting PivotTables and specific fields, as it appears an integrated Value field is targeted and filtered via code differently than, say, a "filter' field I have attached to the top of the PivotTables, where they can accept no "begins with", "contains", etc, strings. They are just checkboxes, and one or multiple can be selected.
So it's one thing for me to tell it via VBA to select one item, and having it select all but one item. The latter requires the code to target every single possible value, but not the one that I want excluded.
My idea for this, then, is to create an array from every possible existing value in this filter field, then going through a loop where each value is added to my code as a value to check.
I have some code so far:
ActiveSheet.PivotTables("QTD_Pivot_By_Category").PivotFields( _
"[Range].[Address_1].[Address_1]").VisibleItemsList = Array( _
"[Range].[Address_1].&", "[Range].[Address_1].&[0]", "[Range].[Address_1].&[101]" _
, "[Range].[Address_1].&[INC]", "[Range].[Address_1].&[KRT]", _
"[Range].[Address_1].&[LTD]", "[Range].[Address_1].&[RPO]", _
"[Range].[Address_1].&[ INC]", "[Range].[Address_1].&[CORP]", _
"[Range].[Address_1].&[INC.]", "[Range].[Address_1].&[LTD.]", _
"[Range].[Address_1].&[LTEE]", "[Range].[Address_1].&[PAWS]", _
Now, if I just record this macro from actions in Excel, and do "select All", then de-select the one I don't want, it will error. It errors because it's selecting ~300 values, and while it's 'writing' this code, it errors when it hits the limit of "_" delimited breaks in one straight line of VBA code.
If my field is called "Address_1" as above, part of the range..."Range" (not sure where that's defined or why, but it works), can I get some help as to the most efficient way to define said ".VisibleItemList" as all POSSIBLE items in the list from a dynamic array rather than needing to be selected manually? This list will be different day-to-day so it can't just be a hardcoded flat list.
Ideally, also in a way that circumvents the max limit on "_" line breaks in a line of code in VBA for Excel.
If it's of any use for context, my table looks like this. See that checkbox drop-down? I want a snapshot of every updated value sitting in there to be put into an array and then iterated upon being added in a way similar to my example code:
Edit:
Since that filter field's values are being pulled from a local datasource, I decided to just grab those and make an array that way! So I'm starting my code this way:
Dim OGDataRange As Range, OGDataLastRow As Long
Dim ValueArray As Variant
OGDataLastRow = Worksheets("DATA QTD").Range("U2").End(xlDown).Row
Set OGDataRange = Worksheets("DATA QTD").Range("U2:U" & OGDataLastRow)
ValueArray = OGDataRange.Value
"ValueArray" is now my array. So I need help one-by-one pulling the values of this array, and adding them to my VisibleItemList as seen above.
Thank you so much for any assistance.
This might help you
Private Sub this()
Dim pf As PivotField
Dim pi As PivotItem
Dim strPVField As String
strPVField = "this"
Set pt = ActiveSheet.PivotTables("PivotTable1")
Set pf = pt.PivotFields(strPVField)
Application.ScreenUpdating = False
Application.DisplayAlerts = False
On Error Resume Next
pf.AutoSort xlManual, pf.SourceName
For Each pi In pf.PivotItems
pi.Visible = False
Next pi
pf.AutoSort xlAscending, pf.SourceName
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
borrowed from
Deselect all items in a pivot table using vba
I'm very new in VBA.. Currently I'm on my project which required me to creating a automation tool by plotting 2 bar chart automatically based on 2 table data as shown below
However as the table data was generated out from another automation tool thus the number of rows can be increase or decrease. And the number of rows could be more/less than 5, I couldn't select the cells by specifying the address of the cell such as "A7" or "B6".
And these are the expected output of the chart:
Hope some kind soul could help me on this..
I don't think you need a macro.
All you need is some dynamic named ranges:
Microsoft Support
And set your data range to those named ranges.
If the different blocks of data in a given worksheet are separated by blank rows and columns, and the bloc you want is the only one containing the word "Date", you could use a simple VBA routine to find the word "Date", determine the block of data is sits within, and insert a chart using that data. Like the following:
Sub PlotRangeUsingFind()
Const SFind As String = "Date"
Dim rng As Range
On Error Resume Next
Set rng = ActiveSheet.Cells.Find(SFind).CurrentRegion
If Not rng Is Nothing Then
With ActiveSheet.Shapes.AddChart.Chart
.ChartType = xlColumnClustered
.SetSourceData rng
End With
End If
End Sub
You can modify this to look for any unique word in the block of data. It need not be the entire cell's contents; for example, you could use "Accepted" based on your screenshot.
So I've been assigned to work on two identical databases which are very poorly made. Both are split up into front/back ends, but that have different data.
Is it possible to combine both databases into one and create a form to allow the user to choose which "database" they will interact with?
To clarify, the school that utilizes these databases has a database for the Fall semester and Spring semester. Both contain the exact same forms/queries/tables, but the specific data differs. e.g. Maybe a teacher teaches 3 classes in the fall, but 4 classes in the Spring (the database is way more complicated that this though). The structure of the data remains the same, but the specific data changes depending on the database.
So, if they need to access data, they need to open the corresponding database. As a result, they are constantly open/closing different databases. What they want to do is be able to just use one database and switch between Fall/Spring.
Is this at all possible?
Without knowing the exact structure of your data it's hard to give specific answers, but my gut feeling is most definitely these two databases could be combined. Combine all the data in tables of the same name, but add a "Semester" column to any table that would result in duplicated data. Then simply create the application frontend so wherever necessary the user would select the Semester they want to view, and pass it to your queries retrieving the data. You could even carry the "Semester" value from the beginning as you point out, and pass it from screen to screen (w/ a global application-level variable).
It's been a while since I've had to code in Access in my professional life, but I'm sure there's a way to accomplish your goal. Maybe post more details of your specific data structure, and we can all jump in and give pointers!
Here is a table linking function that I've used many times over the years. In order to use this, you'll have to provide a couple of functions:
ThisTableShouldBeChanged is passed the name of a table and returns true if the link to the table should be re-linked as part of the database switch. This will allow you to selectively exclude tables from the switch.
GetConnectionString should return the name of the new connection string. Constructing a connection string to another Access database is simple, just put the full database path into this format: ";DATABASE=path"
In order to do what you want, you just have to run this function when the user chooses a new databse to open. One word of advice: I would put an unmistakable element in the UI, or example the background color of the screens, that will signal to the user which database they are working on so there is no confusion.
This function can also be used to link tables to an ODBC or SQL Server connection when deploying into a production environment. That's what I generally use it for.
Public Function LinkTables() As Boolean
On Error GoTo HandleError
Dim tdf As TableDef
Dim tdfNew As TableDef
Dim strName As String
Dim tdfs As TableDefs
Dim strSource As String
Dim strConnect As String
Dim blnThrowErr As Boolean
Dim arr() As Variant
Set tdfs = CurrentDb.TableDefs
Dim intCount As Integer
Dim i As Integer
intCount = tdfs.Count
strConnect = GetConnectionString()
i = 1
'Save a copy of the existing table names'
ReDim arr(tdfs.Count, 2)
For Each tdf In tdfs
strName = tdf.Name
If ThisTableShouldBeChanged(strName) Then
If tdf.Connect <> "" Then
strSource = tdf.SourceTableName
arr(i, 1) = strName
arr(i, 2) = strSource
i = i + 1
End If
End If
Next tdf
tdfs.Refresh
Dim strNameRep As String
'Create new linked tables'
For i = 1 To UBound(arr)
If arr(i, 1) <> "" Then
Set tdfNew = New TableDef
strName = arr(i, 1)
tdfNew.Name = arr(i, 1)
tdfNew.SourceTableName = arr(i, 2)
tdfNew.Connect = strConnect
strNameRep = strName & "_temp"
'rename the old table'
tdfs(strName).Name = strNameRep
tdfs.Refresh
tdfs.Append tdfNew
If Err.Number <> 0 Then
Debug.Print Err.Description
tdfs(strNameRep).Name = strName
Err.Clear
Else
tdfs.Delete strNameRep
End If
On Error GoTo HandleError
End If
Next i
tdfs.Refresh
LinkTables = True
ExitHere:
Exit Function
HandleError:
MsgBox Err.Description & " (" & Err.Number & ")"
LinkTables = False
Resume ExitHere
End Function
I think it should be possible to create queries for each of the tables with the original table name and name the new, combined tables with an extra letter or two. Let us say you have a table Students, the new table becomes JointStudents and you create a query Students:
SELECT F1, F2, F3 FROM JointStudents WHERE Semester=Forms!PickSem!txtSemester
This means that any form or query that refers to the table Students will now pick up the query students with the built in filter.
A least-initial-cost solution, setting up an interface to link to the appropriate semester, is still going to involve touching all the forms and adding another (for user-friendly control of the "active" db), and you'll still be incurring additional cost for any changes needed down the road (as they'll be made in two places rather than one).
You don't have to change the tables by hand: you can write SQL to do it. It's possible to write a procedure that steps through existing tables and adds a "Semester" column and populates it ... best to run it in both databases so that you can also populate the columns in the same procedure. If the tables are named the same in both databases, it would even be possible to run a procedure that would step through all of them, updating Fall, updating Spring, and appending Spring to Fall so that you have everything in one database.
A safer method would probably be to copy the Fall structure to a new table and modify that table, in case something happens and you need to "roll back" what you've done.
Given that this question was asked months ago and you're working with a school, you've probably either resolved the issue somehow or have had to put it aside, but if there's still a need for it, I can post VBA that should help.
I am copying a record from one table to another in Access 2007. I iterate through each field in the current record and copy that value to the new table. It works fine until I get to my lookup column field that allows multiple values. The name of the lookup column is "Favorite Sports" and the user can select multiple values from a dropdown list.
I believe the values of a multivalued field are stored in an array but I cannot access the values in VBA code! I've tried myRecordset.Fields("myFieldName").Value(index) but it didn't work. I don't understand how Access stores multiple values in one field.
I saw something about ItemsSelected on another forum but I don't know what Object is associated with that method.
Thanks for any help!
I would recommend against using multivalue fields for precisely the reason you're running into, because it's extremely complex to refer to the data stored in this simple-to-use UI element (and it's for UI that it's made available, even though it's created in the table design).
From your mention of "ItemsSelected," you seem to be assuming that you access the data in a multivalue field the same way you would in a multiselect listbox on a form. This is not correct. Instead, you have to work with it via a DAO recordset. The documentation for working with multivalue fiels explains how to do it in code, something like this:
Dim rsMyField As DAO.Recordset
Set rsMyField = Me.Recordset("MyField").Value
rsChild.MoveFirst
Do Until rsChild.EOF
Debug.Print rsChild!Value.Value
rsChild.MoveNext
Loop
rsChild.Close
Set rsChild = Nothing
Now, given that you can usually access the properties of a recordset object through its default collections, you'd expect that Me.Recordset("MyField").Value would be returning a recordset object that is navigable through the default collection of a recordset, which is the fields collection. You'd think you could do this:
Me.Recordset("MyField").Value!Value.Value
This should work because the recordset returned is a one-column recordset with the column name "Value" and you'd be asking for the value of that column.
There are two problems with this:
it doesn't actually work. This means that Me.Recordset("MyField").Value is not reallly a full-fledged recordset object the way, say, CurrentDB.OpenRecordset("MyTable") would be. This is demonstrable by trying to return the Recordcount of this recordset:
Me.Recordset("MyField").Value.Recordcount
That causes an error, so that means that what's being returned is not really a standard recordset object.
even if it did work, you'd have no way to navigate the collection of records -- all you'd ever be able to get would be the data from the first selected value in your multivalued field. This is because there is no way in this shortcut one-line form to navigate to a particular record in any recordset that you're referring to in that fashion. A recordset is not like a listbox where you can access both rows and columns, with .ItemData(0).Column(1), which would return the 2nd column of the first row of the listbox.
So, the only way to do this is via navigating the child DAO recordset, as in the code sample above (modelled on that in the cited MSDN article).
Now, you could easily write a wrapper function to deal with this. Something like this seems to work:
Public Function ReturnMVByIndex(ctl As Control, intIndex As Integer) As Variant
Dim rsValues As DAO.Recordset
Dim lngCount As Long
Dim intRecord As Integer
Set rsValues = ctl.Parent.Recordset(ctl.ControlSource).Value
rsValues.MoveLast
lngCount = rsValues.RecordCount
If intIndex > lngCount - 1 Then
MsgBox "The requested index exceeds the number of selected values."
GoTo exitRoutine
End If
rsValues.MoveFirst
Do Until rsValues.EOF
If intRecord = intIndex Then
ReturnMVByIndex = rsValues(0).Value
Exit Do
End If
intRecord = intRecord + 1
rsValues.MoveNext
Loop
exitRoutine:
rsValues.Close
Set rsValues = Nothing
Exit Function
End Function
Using that model, you could also write code to concatenate the values into a list, or return the count of values (so you could call that first in order to avoid the error message when your index exceeded the number of values).
As cool as all of this is, and as nice as the UI that's presented happens to be (it would be really nice if they'd added selection checkboxes as a type for a multiselect listbox), I'd still recommend against using it precisely because it's so much trouble to work with. This just takes the problem of the standard lookup field (see The Evils of Lookup Fields in Tables) and makes things even worse. Requiring DAO code to get values out of these fields is a pretty severe hurdle to overcome with a UI element that is supposed to make things easier for power users, seems to me.
For a quick and dirty way of getting the values out of a multivalued ('complex data') column, you can use an ADO Connection with the Jet OLEDB:Support Complex Data connection property set to False e.g. the connection string should look something like this:
Provider=Microsoft.ACE.OLEDB.12.0;
Data Source=C:\dbs\TestANSI92.accdb;
Jet OLEDB:Engine Type=6;
Jet OLEDB:Support Complex Data=False
The multivaled type column will now be of type MEMO (adLongVarWChar) with each value separated by a semicolon ; character.
But that's only half the problem. How to get data into a multivalued column?
The Access Team seem to have neglected to enhance the Access Database Engine SQL syntax to accommodate multivalued types. The 'semicolon delimiter' trick doesn't work in reverse e.g.
INSERT INTO TestComplexData (ID, weekday_names_multivalued)
VALUES (5, 'Tue;Thu;Sat');
fails with the error, "Cannot perform this operation", ditto when trying to update via ADO recordset :(