ACCESS VBA Comparing a string against a string in another table - ms-access

here's my problem. I have 2 tables
tvehicle and tPMCL
the tVehicle table has a list of our vehicles, and the tPMCL holds when preventitive maint. is done.
tvhhicle.VehicleTagnumber holds the actual plate number, and tPMCL.Tag holds only a Index of a look up of that number from when it was entered, I wish it had the tag number so when i do loops through my data comparing it would be able to match up, as it is:
it's comparing something along the lines of "XPE 269" to 1 and that's not working so well.
Any ideas? the answer may not be a VBA answer it may be a diferent way to do the lookup in the first place. But I just can't find another way to do the lookup and actually store the plate number and not an index of it.

It appears that what you think of as Index is actually a foreign key. This is a good thing. This means that if VehicalTagNumber where to change (e.g. a bad input) the referring table would not need to be updated.
If you need to loop through the tPMCL and you need the corresponding Tag Number you can do one of two things.
You could use Dlookup to get it on each loop. e.g.
Dim strTag As String
strTag = DLookup("[VehicleTagnumber]", "tvhhicle","[Id] = 1")
However this will be slow for large numbers of records.
Instead just base your record set on a SQL statement that joins the two table instead of opening a table directly.
Dim dbVehicle As Object
Dim rstVehicle As Object
Dim fldEnumerator As Object
Dim fldColumns As Object
Dim strSQL as String
Set dbVehicle = CurrentDb
Set rstVehicle = dbVehicle.OpenRecordset("tVehicle")
Set fldColumns = rstVehicle.Fields
strSQL = "SELECT * FROM tMPCL m INNER JOIN tVehicle v ON m.Tag = v.ID"
Set rsttPMCL = dbVehicle.OpenRecordset(strSQL)

Related

Updating an Access Table with a CSV File Automatically

Problem Background:
I have a Powershell script that I can execute from my Microsoft Access Form that scans through file folders that contain information on different facilities, and produces a CSV that looks something like:
SiteCode FacilityNumber DocumentType HyperlinkPath
DKFZ 10 DD1400 C:\FACILITIES DATABASE\path
DKFZ 10 FLRPLN C:\FACILITIES DATABASE\path
SMQL 17 P1 C:\FACILITIES DATABASE\path
SMQL 17 P2 C:\FACILITIES DATABASE\path
So that way every time new files are added to those folders, I can just run this script and produce an updated list of everything I have:
C:\...\Output\scanResults.csv
All I need now is to take that CSV file and update (or even overwrite) a Table that I have in an Access database, which has relationships to other tables and is used by various Queries and Forms in the database. The CSV columns are already named and formatted in the same way as the Access Table.
I've looked at and tried to replicate the following threads:
VBA procedure to import csv file into access
Access Data Project Importing CSV File In VBA
VBA Import CSV file
The closest answer I found is:
Sub Import()
Dim conn as new ADODB.Connection
Dim rs as new ADODB.Recordset
Dim f as ADODB.field
conn.Open "DRIVER={Microsoft Text Driver (*.txt; *.csv)};DBQ=c:\temp;"
rs.Open "SELECT * FROM [test.txt]", conn, adOpenStatic, adLockReadOnly, adCmdText
While Not rs.EOF
For Each f In rs.Fields
Debug.Print f.name & "=" & f.Value
Next
Wend
End Sub
But this obviously won't write the data into the table, and I could not understand what the author was trying to say with respect to changing Select to Insert.
I've also found:
DoCmd.TransferText acImportDelim, "YourCustomSpecificationName", _
"tblImport", "C:\SomeFolder\DataFile.csv", False
Since both of these are from 2010, I wonder if there isn't a better way to accomplish this in Access 2013. And while I can do this all manually, I would like to incorporate it into the VBA code I use to tell Powershell to produce the CSV, that way I can make it and then upload it immediately.
Any help or suggestions are greatly appreciated. I'm still very green to Access, VBA, and SQL statements in general, so this has been very much a "learning as I go" process.
I prefer to use SQL clauses and queries to import such data. The details depend on your exact configuration, but it tends to look something like this:
SELECT *
INTO MyTable
FROM [Text;FMT=CSVDelimited;HDR=No;DATABASE=C:\...\Output].[scanResults#csv]
Or append the information to the table instead:
INSERT INTO MyTable
(SiteCode, FacilityNumber, DocumentType, HyperlinkPath)
SELECT *
FROM [Text;FMT=CSVDelimited;HDR=No;DATABASE=C:\...\Output].[scanResults#csv]
This allows you to do checks before importing (using a WHERE clause), import only specific values, and allows you to customize a lot without using external files.
DATABASE= is followed by your folder name (use {} if there are characters that need escaping in there), and then followed by your file name with . replaced with #.
You can execute it by either saving it as a query, or using it as a string in either VBA or a macro. Note that I rarely recommend macro's, but you can execute them using a scheduled task and close Access after importing.
To backup and restore a relation before and after updating, you can use the following functions:
Public Function DeleteRelationsGiveBackup(strTablename As String) As Collection
Dim ReturnCollection As Collection
Set ReturnCollection = New Collection
Dim i As Integer
Dim o As Integer
Do While i <= (CurrentDb.Relations.Count - 1)
Select Case strTablename
Case Is = CurrentDb.Relations(i).Table
ReturnCollection.Add DuplicateRelation(CurrentDb.Relations(i))
o = o + 1
CurrentDb.Relations.Delete CurrentDb.Relations(i).NAME
Case Is = CurrentDb.Relations(i).ForeignTable
ReturnCollection.Add DuplicateRelation(CurrentDb.Relations(i))
o = o + 1
CurrentDb.Relations.Delete CurrentDb.Relations(i).NAME
Case Else
i = i + 1
End Select
Loop
Set DeleteRelationsGiveBackup = ReturnCollection
End Function
Public Sub RestoreRelationBackup(collRelationBackup As Collection)
Dim relBackup As Variant
If collRelationBackup.Count = 0 Then Exit Sub
For Each relBackup In collRelationBackup
CurrentDb.Relations.Append relBackup
Next relBackup
End Sub
Public Function DuplicateRelation(SourceRelation As Relation) As Relation
Set DuplicateRelation = CurrentDb.CreateRelation(SourceRelation.NAME, SourceRelation.Table, SourceRelation.ForeignTable)
DuplicateRelation.Attributes = SourceRelation.Attributes
Dim i As Integer
Dim fldLoop As Field
Do While i < SourceRelation.Fields.Count
Set fldLoop = DuplicateRelation.CreateField(SourceRelation.Fields(i).NAME)
fldLoop.ForeignName = SourceRelation.Fields(i).ForeignName
DuplicateRelation.Fields.Append fldLoop
i = i + 1
Loop
End Function
And then, when importing:
Dim colRelBackup As Collection
Set colRelBackup = DeleteRelationsGiveBackup("MyTable")
'Delete MyTable
'Import new version
RestoreRelationBackup colRelBackup
(Note that the code is quite long, developed for a project several years ago, and not extensively tested. If a field name/type is not exactly like how it was before the import, the restore of the backup might fail and the relations will be permanently lost).
So some high level architect advice: replacing data versus replacing table
It is easier replacing data - - the new incoming data must be the exact same structure as the existing table (i.e. same field names and no new fields).
just fire a Delete Query to the existing table that clears out all records
then fire an Append Query to the linked CSV file that writes all those records into the existing table
very simple really.
You can replace the tables if you must - and you are already down this path. You can delete those table relationships entirely. That table relationship feature is useful - but not mandatory. You can create relationships at the query level as an alternative. Essentially the table relationships just auto create the query level relationships. If you delete the table relationships then one must simply create the table relationships at the query level manually - they don't automatically appear. Note however that if one is relying on cascade deletes or referential integrity, then removing table relationships will undo that - so you should check these points.
Deleting Table Relationships will not break any existing queries. Their table relationship join lines will remain intact.

MS Access 2007: Runtime error 64224: Method 'Collect' of object 'recordset2' failed

I have been searching high and low for a workaround for this error, but since I haven't, I now turn to you for help. I am not (at all) an experienced programmer, so I hope this is not too basic a question to ask here, and that others can still benefit.
I am trying to set up a form, where I can register when one of our employees lends some stuff for a given period of time. I.e. from 21-12-2015 to 10-01-2016 employee n.n. lends a laptop. The form has two unbound textboxes TxtFraDato and TxtTilDato for the dates (from and to), and two unbound listboxes containing the name of the employee and the 'stuff' to be lend. The listboxes get their data from the tables:
Medarbejder (The employee):
- Navn: text (Primary key)
Materiale:
- Id: number (Primary key)
- ModelType: Text
I know that having a table like my Medarbejder-table is not at all pretty, and I have a fealing that this could be causing the problem, but this table is linked to a .txt-file created from a bat-file, and I have not been able to figure out, if there is another way to create such a link, that will give me a chance to give the table another primary key.
Finally, I have a button that on-click should save the record to the table "Udlaan":
Id: number (Primary key)
FraDato: date
TilDato: date
Medarbejder: text (lookup column)
Materiale: number (lookup column)
However, when I click the button, I get the runtime error 64224: Method 'Collect' of object 'recordset2' failed.
Private Sub btnSaveUdlaan_Click()
Dim db As Database
Dim rs As Recordset
Dim FraDato As Date
Dim TilDato As Date
Dim Medarbejder As String
Dim Model As Integer
Set db = CurrentDb
Set rs = db.OpenRecordset("Udlaan")
FraDato = Me.TxtFraDato.Value
TilDato = Me.TxtTilDato.Value
Medarbejder = Me.MAList.Column(0)
Model = Me.ModelList.Column(0)
AddUdlaan rs, FraDato, TilDato, Medarbejder, Model
rs.Close
db.Close
End Sub
The AddUdlaan:
Private Sub AddUdlaan(rs As Recordset, FraDato As Date, _
TilDato As Date, Medarbejder As String, Model As Integer)
' Adds a new record to a Recordset using the data passed
' by the calling procedure.
With rs
.AddNew
!FraDato = FraDato
!TilDato = TilDato
**!Medarbejder** = Medarbejder ß The debugger is pointing to this field
!Materiale = Model
.Update
End With
End Sub
Perhaps this is not at all the right way to solve by problem, so any help on this is much appreciated. But as it is, I would also be happy to lean something from the encounter with this error.
This is because the lookup field is a recordset of the recordset. Only Recordset2 can handle these.
Thus, what you should assign is the (hidden) Id of Medarbejder, in pseudo code:
!Medarbejder = <Id of recordset Medarbejder>
This can be done but - as already mentioned by #Parfait - do yourself a favour and get rid of the lookup fields. How "smart" they may seem, they will cause you nothing than trouble in the future.
As a start, change table Medarbejder to have an AutoNumber Id as the primary key.

Access VBA Search Loop

I am currently making an search loop in VBA for my database. In this database I Have 2 Tables, one with the customers records called Main and another one with search words called Sparr. The idea is to use a filter to filter out the customers that match any of the search words in the Sparr table. Any customer that does not match any of the search words gets added to another table called filteredCustomerT.
For example:
table "Main"
Field "Mail"
mike#coolmail.com
john#hotmail.com
dave#mail.com
jonny#mailx.com
table "Sparr"
Field "sparrord"
hotmail
jonny
table "Testtable"
Field "testMail"
mike#coolmail.com
dave#mail.com
So if I run this VBA code I want john#hotmail.com and jonny#mailx.com to be filtered out. The Main table contains 200k records and the Sparr table contains 2k search words. I have wrote some VBA code that should loop through the Main table. For every record in the Main table a have another nested loop that loops through the Sparr table so see if there is any match. If there is not a match the VBA code copies the entry to anther table called Testtable. I Use the inStr function to do the matching.
Below I have posted the VBA code that does not seem to work. Can anyone help me ant maybe point out a fault in the code. I am very new to VBA programming.
Option Compare Database
Option Explicit
Sub filter()
Dim mainMail As Recordset
Dim sparrSokord As Recordset
Dim testtableTestmail As Recordset
Dim mainTemp As String
Dim sparrTemp As String
Dim match As Integer
Set mainMail = CurrentDb.OpenRecordset("Main")
Set sparrSokord = CurrentDb.OpenRecordset("Sparr")
Set testtableTestmail = CurrentDb.OpenRecordset("Testtable")
Do Until mainMail.EOF
mainTemp = mainMail![Mail]
match = 0
sparrSokord.MoveFirst
Do Until sparrSokord.EOF
sparrTemp = sparrSokord![sparrord]
If (InStr(mainTemp, sparrTemp) <> 0) Then
match = 1
Exit Do
End If
sparrSokord.MoveNext
Loop
If (match = 0) Then
testtableTestmail.AddNew
testtableTestmail![testMail] = mainTemp
testtableTestmail.Update
End If
mainMail.MoveNext
Loop
End Sub
InStr can operate in unexpected ways is you have nulls/empty strings/etc, involved.
I've noticed that you have nothing resetting the position of SearchwordWord back to the beginning of the record set once you reach the end.
Naturally you would do something like SearchwordWord.MoveFirst before the Do Until SearchwordWord.EOF. It doesn't hurt to do one before the Do Until customerMail.EOF either
I'll also note the Do Until always executes the contents of the loop, and then checks the condition at the end (which could be giving you unexpected results, especially with SearchwordWord being at EOF after the first successful pass of the loop.
You probably want to use a while/wend instead for both do untils (I practically never use them as it is). This is probably the biggest cause of your grief.
The problem is now solved. I just had to close some programs and try again with the updated code above. Worked just fine!

Using additional tables as lookup values in Access Query in IIF Statements?

I'm currently working with an Access(2010) Query that is taking multiple order items and GROUPING BY the order number. For example, a customer may have ordered 5 items under order number 123 but my query groups all of these individual items into one line with each item in it's own column. The objective is each individual order number is contained on one line because the query will be exported in a .csv format to 3rd party software.
Since I'm combining multiple records/items I'm running into issues when the description field is getting truncated based on 255 characters. Since working in a query I don't see the option of changing the text field to a memo field, so that won't resolve the issue and the GROUP BY clause is capping all the text fields at 255 chars anyway.
So, my question is instead of using the description fields from the current query is there a way that I can use an additional table to lookup an items description based on the part number ordered? Proposed new table would be something very simple like:
PART | DESC
123 Widget Z_Langs_AUS_INT<br>
567 Widget K_Langs_DOM_CAN<br>
890 Widget B_Langs_SM
So the ideal statement in the query would be something like IIF TABLE1.PART#ORDERED(from current query) = NEWTABLE.PART(from new table), then obtain NEWTABLE.DESC(from new table) otherwise ""
This would return the description of a specific part number if the part number fields matched otherwise leave the field blank because it likely doesn't contain a part number.
I'd appreciate any suggestions at this point. I may be going about this all wrong when trying to resolve this issue so fresh input is welcome.
Don't write that as a query. It's too complex to bother with, and is extremely simple to do with VBA and direct text access. Add a reference to the Microsoft Scripting Runtime (scrun.dll), use internal query objects for your SQL data, and use code similar to what's below.
Sub makeText()
Dim rs1 As Recordset, rs2 As Recordset
Dim oFSO As Scripting.FileSystemObject, oFile As Scripting.TextStream
Dim txtLine As String
Set oFSO = New Scripting.FileSystemObject
oFile = oFSO.CreateTextFile("FileName.csv")
Set rs1 = CurrentDb.OpenRecordset("baseQuery")
Do Until rs1.EOF
txtLine = rs1!OrderNumber.Value
Set rs2 = CurrentDb.OpenRecordset("innerQuery")
Do Until rs2.EOF
txtLine = txtLine & "," & rs2!itemDesc.Value
rs2.MoveNext
Loop
oFile.WriteLine txtLine
rs1.MoveNext
Loop
oFile.Close
End Sub
Below is a picture from an example database we used to use. It has our parts with all the info you could need, and then the part that matches up with our vendor. The query used will display each and every row where our part matches the vendor part.
You can also just put a [AskMeForMyPart] or whatever in square brackets like that to make the query pull based on that specific part.

how do i determine what the next record number (PK) is?

im trying to get the next number in the autonumber sequence for the primary key programatically. For instance, if the last number in the table was 10, i need it to return 11. Before, I would use something like:
docmd.RunCommand acCmdRecordsGoToNew
in order to tell the database to go to the next record, and then i'd assign it to a control on the form to show the user what record they are currently entering. The problem is, this function ceased to work when I disabled the navigation buttons by setting it's property to "No" in the properties window. How do I get the next record in vba without the nav bar being enabled?
To know what the real next value is, you have to look up the SeedValue for your Autonumber column. This code does that:
Public Function GetSeedValue(strTable As String, strColumn As String) As Long
Dim cnn As Object 'ADODB.Connection
Dim cat As Object ' New ADOX.Catalog
Dim col As Object ' ADOX.Column
Set cnn = CurrentProject.Connection
Set cat = CreateObject("ADOX.Catalog")
cat.ActiveConnection = cnn
Set col = cat.Tables(strTable).Columns(strColumn)
GetSeedValue = col.Properties("Seed")
Set col = Nothing
Set cat = Nothing
Set cnn = Nothing
End Function
If you're going to call it a lot, you'd likely want to cache the ADOX Catalog object variable, rather than re-initialize it each time you call this function.
Note that in a multiuser environment, this may or may not be accurate, since by the time you use it, it may have been updated by another user. However, it doesn't have the problem with skipping Autonumber values that Max()+1 can have.
Keep in mind, though, that if you care about the next Autonumber value, it means YOU'RE USING IT WRONG. Autonumber values are surrogate keys and you should never, ever care what the values are.
Turns out that there is a VBA function that will interact with the database and actually return a value. This is what I ended up doing to get the next record number:
Dim nextRecord As Integer
nextRecord = DMax("ID", "my_table") + 1
Sloppy, but effective for my single client situation. There is also a where clause that can be applied:
Dim nextRecord As Integer
nextRecord = DMax("ID", "my_table", "field = value")