Transferring using ADO 5 worksheets into 5 tables - ms-access

I have been struggling to transfer data from multiple excel worksheets into multiple access tables. So how this goes is this way. I have 5 worksheets and each of this worksheet is to be transferred from Excel into a specific Access table. How do I do this using VBA?
I cant seem to put the file in so I hope you guys understand!
Thanks in advance for helping me!!

You can use ADO. First, set a reference to the ADO library in the VBE: Tools, References. Look for Microsoft ActiveX Date Objects Library 6.1 (or 6.0) and tick the box next to it.
Then you can use the code below to post data from a sheet to a table in the Access database (use this in a loop if you want to do multiple sheets):
Dim i As Long, j As Long
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim arr() As Variant
'Load the data from a sheet into an array
arr = Sheets(1).Range("A2:B10").Value
'Connect to Access database
Set cn = New ADODB.Connection
With cn
.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Documents\Database1.accdb"
.Open
End With
'Post data to table
Set rs = New ADODB.Recordset
With rs
.Source = "Table1"
.ActiveConnection = cn
.CursorType = adOpenStatic
.CursorLocation = adUseServer
.LockType = adLockOptimistic
.Open
For i = 1 To UBound(arr, 1)
.AddNew
For j = 1 To UBound(arr, 2)
.Fields(j).Value = arr(i, j) 'This assumes you have an autonumber ID field. (Field indexes in recordsets are 0 based.)
Next
.Update
Next
.Close
End With
'Clean up
Set rs = Nothing
cn.Close
Set cn = Nothing
EDIT:
If you want to check if a record already exists in the table, use the recordset FILTER property. Say you have an "ID" in column 1 of your spreadsheet and an "ID" field in your database table, then do:
rs.Filter = "ID='" & arr(1,j) & "'"
If rs.RecordCount > 0 then
'Record(s) already exist
...

Related

Read only recordset "local" update

I have DAO recordset that is generated with pass-through query to postgresql stored function. I use it to fill out combobox in my form. What I need is additional item in combobox with "AllItems" description. But the recordset is read-only (that's normal in this case). So I cannot just add new row to it. Can I do any kind of in memory recordset clone, copy or anything like that to make addition possible? I don't want to update recordsource. And I don't want to hardcode this option in to the pgsql function as well.
Public Sub fillCboAssortmentType()
Dim rs As DAO.Recordset
If (lngViewContext = acMyItems) Then
Set rs = getAssortmentTypesByDAO(TempVars!loggedUser)
Else (lngViewContext = acAllItems) Then
Set rs = getAssortmentTypesByDAO
End If
' It wont work, because the rs is RO
With rs
.AddNew
!type_id = 0
!type_name = "***AllItems***"
End With
' It wont work neither, because cboTypeFilter rowsource is Table/Query
Set Me.cboTypeFilter.Recordset = rs
Me.cboTypeFilter.AddItem "0;***AllItems***"
End Sub
Any suggestions?
TY All.
I think you are asking for a "In Memory" Recordset. Let's assume you have a table which looks like this
Then the following code will read the values from the table and copy it to a in memory recordset and add a new value but only in memory
Option Compare Database
Option Explicit
Sub inMemory()
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
With rs.Fields
.Append "val", adVarChar, 64
End With
Dim sourceRs As DAO.Recordset
Dim db As DAO.Database
Set db = CurrentDb
Set sourceRs = db.OpenRecordset("SELECT * FROM tbl")
Dim i As Long
rs.Open
Do Until sourceRs.EOF
rs.AddNew
rs.Fields(0).Value = sourceRs.Fields(0).Value
rs.Update
sourceRs.MoveNext
Loop
rs.AddNew
rs.Fields(0).Value = "Cancel"
rs.Update
' let's print the list just for testing
rs.MoveFirst
Do Until rs.EOF
Debug.Print rs.Fields(0).Value
rs.MoveNext
Loop
End Sub

Export Excel Range to Access table VBA

I want to have a button on the Excel spreadsheet and have the data copied to the Access table.
The range is an auto-populated field from another sheet in the same workbook.
I tried few codes to make this happen, but I either get an error 1004: application-defined or object-defined error, or no error but data not being copied in Access DB.
My code is copied below.
Sub Export_Data()
Dim cnn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim dbPath
Dim x As Long, i As Long
dbPath = "H:\RFD\RequestForData.accdb"
Set cnn = New ADODB.Connection
cnn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & dbPath
Set rst = New ADODB.Recordset 'assign memory to the recordset
rst.Open Source:="tblRequests", ActiveConnection:=cnn, _
CursorType:=adOpenDynamic, LockType:=adLockOptimistic, _
Options:=adCmdTable
rst.AddNew
For i = 1 To 13
rst(Cells(1, i).Value) = Cells(x, i).Value
Next i
rst.Update
rst.Close
cnn.Close
Set rst = Nothing
Set cnn = Nothing
MsgBox " The data has been successfully sent to the access database"
Set rst = Nothing
Set cnn = Nothing
End Sub
Looking at your Subroutine I see two things that can make it not to work:
rst(Cells(1, i).Value) = Cells(x, i).Value <- Where is 'x' initialized?
There is only one loop that moves over the fields but I think it should be another loop for the rows in the Excel.
With this two changes, the loop when the records are save could become something like this:
For x = 1 TO lastRow ' Last row has to be calculated somehow
rst.AddNew
For i = 1 To 13
rst(Cells(1, i).Value) = Cells(x, i).Value
Next i
rst.Update
Next x
Hope it helps.

How to update multiple tables in MS Access from multiple excel files?

Every month I have to UPDATE my MS Access db from approximately 30 excel files. They have all the same structure and format. I try to modify this code several times to update each tables in my db in once but i didn't succeed. (found in this forum)
I have 3 questions:
How is it possible to say to the vba code to look at this range in the excel template which correspond to this column in the MS Access db?
How can i make this vba code update all the tables in once based on the Primary Key?
Then, Is it possible to select the Folder where all these excel files are and the code will loop through all files?
Public Sub UpdatePriceList()
Dim cn As ADODB.Connection, rs As ADODB.Recordset
Dim sProduct As String, sVariety As String, cPrice As Variant
' connect to the Access database
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; " & _
"Data Source=C:\Users\Gord\Desktop\Database1.accdb;"
' open a recordset
Set rs = New ADODB.Recordset
rs.Open "PriceList", cn, adOpenKeyset, adLockOptimistic, adCmdTable
Range("A2").Activate ' row 1 contains column headings
Do While Not IsEmpty(ActiveCell)
sProduct = ActiveCell.Value
sVariety = ActiveCell.Offset(0, 1).Value
cPrice = ActiveCell.Offset(0, 2).Value
rs.Filter = "product='" & sProduct & "' AND variety='" & sVariety & "'"
If rs.EOF Then
Debug.Print "No existing record - adding new..."
rs.Filter = ""
rs.AddNew
rs("product").Value = sProduct
rs("variety").Value = sVariety
Else
Debug.Print "Existing record found..."
End If
rs("price").Value = cPrice
rs.Update
Debug.Print "...record update complete."
ActiveCell.Offset(1, 0).Activate ' next cell down
Loop
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
thank you in advance!
If you know how to deal with a single excel file and convert it to acess table and all the rest of file will be treated the same way. You can use a bot which will automate the task and do it quickfast. You just have to give it the "instruction" how to deal with single file and it will do the rest.
The instruction can be as simple as a notepad file with table containing the mouse positions and the stroken keyboards key necessary to deal with one file.
You may use "Autoit V3 script"

Duplicating just one record on import

This may seem like a rather odd question but I would like to duplicate the first record (preferably to the next spot in the database). I want to do this as our clients are asking for samples of our mail merge and it has to be a live file. I currently use a dialog box to import the file and most clients are standard comma delimited .txt files.
Private Sub Command38_Click()
Dim f As Object
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim strUpdate As String
Dim strFile As String
Dim strFolder As String
Dim varItem As Variant
Dim P As String
Dim DeleteEverything As String
DoCmd.SetWarnings False
DeleteEverything = "DELETE * FROM [tcppltr]"
DoCmd.RunSQL DeleteEverything
Set f = Application.FileDialog(3)
f.AllowMultiSelect = True
f.InitialFileName = "G:\access\TCPP\"
f.Filters.Clear
f.Filters.Add " Armored TXT Files", "*.asc"
If f.Show Then
For Each varItem In f.SelectedItems
strFile = Dir(varItem)
strFolder = Left(varItem, Len(varItem) - Len(strFile))
P = strFolder & strFile
DoCmd.TransferText acImportDelim, "TCPP Import Specification", "tcppltr", P, False
Next
End If
End Sub
My first idea was to just have the mail merge print a duplicate of the first record which would be better as we don't need duplicates of the shipping labels and everything else that will come from this record but I wasn't sure there was even a way to do that only for the mail merge without creating a separate table with the duplicate record for use only with the mail merge. That seemed terribly inefficient in my opinion.
I am open to other suggestions on how to do this besides just duplicating a record on import.
Thanks in advance for everyone's time and help in this matter!
What this basically does is open two recordsets. The first recordset points to the record you want to duplicate (in this case I did a MoveFirst you can specifically pick whichever record or modify this code to duplicate multiple records). Currently this only copies 1 record.
Dim db As Database
Dim rs1 As Recordset, rs2 As Recordset
Dim i As Long
Set db = CurrentDb
Set rs1 = db.OpenRecordset("Table1", dbOpenSnapshot)
Set rs2 = db.OpenRecordset("Table1", dbOpenDynaset)
rs1.MoveFirst
rs2.AddNew
For i = 1 To rs2.Fields.Count - 1
rs2.Fields(i) = rs1.Fields(i)
Next
rs2.Update
rs1.Close
rs2.Close
If you want an SQL solution I think you need to know before hand the names of all the fields and use an SELECT and INSERT

In Memory, Stand-Alone, Disconnected ADO Recordset

I'm running this code on my datasheet subform when my form loads and I'm not getting any error messages or code breaks. My debug.print shows that the Recordset rs is filled with 2131 records like it should be, but my form shows a single row with #Name? in every field. The control source properties on my controls most certainly do match the field names I have listed above. RS is a form level variable and I'm not closing it or setting it to nothing until the form closes.
Any idea what am I doing wrong?
Set rs = New ADODB.Recordset
rs.Fields.Append "TimesUsed", adInteger
rs.Fields.Append "strWorkType", adVarWChar, 150
rs.Fields.Append "DateLastUsed", adDate
rs.Fields.Append "SelectedYN", adBoolean
Set rs.ActiveConnection = Nothing
rs.CursorLocation = adUseClient
rs.LockType = adLockBatchOptimistic
rs.Open
Dim sSQL As String
sSQL = "MyComplicated SQL Statement Ommitted from this SO Question"
Dim r As DAO.Recordset
Set r = CurrentDb.OpenRecordset(sSQL, dbOpenDynaset, dbSeeChanges)
If Not (r.EOF And r.BOF) Then
r.MoveFirst
Dim fld
Do Until r.EOF = True
rs.AddNew
For Each fld In r.Fields
rs(fld.Name) = r(fld.Name).value
Next
rs.Update
r.MoveNext
Loop
End If
r.Close
Set r = Nothing
Debug.Print rs.RecordCount '2131 records
Set Me.Recordset = rs
OK, so I just read this on the MSDN site:
The recordset must contain one or more fields that are uniquely indexed, such as a table's primary key.
(Note: This information seems to be erroneous in this context.)
is it possible to setup a primary key on a recordset that is only an in-memory object?
Yes, use adFldKeyColumn as the Attrib to the Append Method. Read about FieldAttributeEnum for more details.
If you already have a suitable unique field (or combination of fields) available from your SQL statement, use that. If not, create a long integer field and use it as a fake primary key field ... increment the value for each row you insert.
rs.Fields.Append "pkey", adInteger, , adFldKeyColumn
Also see if this article from Database Journal by Danny Lesandrini is helpful: Create In-Memory ADO Recordsets
I found out that the only way I can make this work is to use LockType adLockPessimistic or adLockOptimisic. adLockReadOnly doesn't work for obvious reasons and for some reason adLockBatchOptimistic does not allow records to display in my form even though the recordset appears to be fully functional.
I also found out that you do not have to have a primary key defined for this type of disconnected Recordset to be bound to a form. I'm sure you won't be able to make any edits or updates to the recordset via the form but in my testing I found that I couldn't make any edits to this type of form/recordset anyway because I was getting Error 3270 (something to do with a missing property). That's really outside the scope of this question.
Here's the minimum amount of code needed to create a working in-memory recordset:
Dim rs As ADODB.Recordset 'Form Level variable
Private Sub Form_Load()
Set rs = New ADODB.Recordset
rs.Fields.Append "ID", adInteger
'Set rs.ActiveConnection = Nothing 'Not Required
'rs.CursorType = adOpenKeyset 'Not Required
'rs.CursorLocation = adUseClient 'Not Required
rs.LockType = adLockPessimistic 'May also use adLockOptimistic
rs.Open
Dim i as Integer
For i = 1 To 10
rs.AddNew
rs("ID").Value = i
rs.Update
Next i
Set Me.Recordset = rs
End Sub
It first appeared to me that binding a form (datasheet view in my case) to this type of disconnected recordset would be a good, simple solution for my particular needs. However, I ran into several problems. The default form sorting does not appear to work when you have your form bound to an ADO recordset. Also, for some reason I never could get this recordset to be editable/updateable which was a requirement for my needs (I was basically using it as a multi-check list). If you obtain the recordset from a table (even if it's an empty table) and then disconnect you can work around this problem. Apparently the table supplies some kind of structure or properties that I've failed to set in my code above, judging by the 3270 error message I get when I try to add/edit a record. And I haven't figured out what those properties are or how to set them.
In conclusion, I think I'll resort to using an Access "temp" table instead since it will be less complicated and not have the problems I've just listed above.
Note: I was able to get everything to work correctly along with inserting new records
by using the example shown above at
Create In-Memory ADO Recordsets
Then changing the following to the forms code...
'Note: The trick was to use rstADO.MoveFirst & rstADO.MoveLast after the rstADO.Update
Option Compare Database
Dim rstADO As ADODB.Recordset
Dim lngRecordID As Long
Private Sub Form_BeforeInsert(Cancel As Integer)
lngRecordID = lngRecordID + 1
rstADO.AddNew
rstADO("EmployeeID").value = lngRecordID
rstADO.Update
rstADO.MoveFirst
rstADO.MoveLast
End Sub
Private Sub Form_Load()
Dim fld As ADODB.Field
Set rstADO = New ADODB.Recordset
With rstADO
.Fields.Append "EmployeeID", adInteger, , adFldKeyColumn
.Fields.Append "FirstName", adVarChar, 10, adFldMayBeNull
.Fields.Append "LastName", adVarChar, 20, adFldMayBeNull
.Fields.Append "Email", adVarChar, 64, adFldMayBeNull
.Fields.Append "Include", adInteger, , adFldMayBeNull
.Fields.Append "Selected", adBoolean, , adFldMayBeNull
.CursorType = adOpenKeyset
.CursorLocation = adUseClient
.LockType = adLockPessimistic
.Open
End With
Set Me.Recordset = rstADO
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set rstADO = Nothing
End Sub