MS access VBA code for Excel file leaves it locked open [duplicate] - ms-access

I've been running into issues with this code. It works fine if I restart the computer and run it, but once the code has been run once it starts to cause errors. I will either get either the "save error" or the "admin error" because the file (either the original or the other) is un-accessible. I can sometimes close down background excel programs from the task-manager to fix it (but not always)
The code's purpose is to download an excel sheet off the internet and add the new rows (and update the old rows) to an ms-access Database.
Whats peculiar is I haven't been able to see any trend with the logical errors.
Const localSaveLocation = ########
Const NetworkDSRTLocation = ########
Private Sub download_btn_Click()
Dim xlsApp As Excel.Application
Dim xlsBook As Excel.Workbook
Dim xlsSheet As Excel.Worksheet
On Error GoTo adminError
Set xlsBook = Workbooks.Open(NetworkDSRTLocation)
Set xlsApp = xlsBook.Parent
On Error GoTo 0
' go to the ERS tab of the workbook, delete the first 3 rows
Worksheets("ERS").Select
Set xlsSheet = xlsBook.Worksheets("ERS")
For row_ctr = 1 To 3
xlsSheet.Rows(1).EntireRow.Delete
Next row_ctr
'set up 'ERS' named range for all cells in this worksheet
xlsSheet.UsedRange.Select
col_count = Cells(1, Columns.Count).end(xlToLeft).Column
row_count = (xlsSheet.Cells(Rows.Count, 1).end(xlUp).Row) + 1
ActiveWorkbook.Names.Add name:="ERS", RefersToR1C1:="=ERS!R1C1:R" & row_count & "C" & col_count
On Error GoTo saveError
Kill localSaveLocation
xlsBook.SaveAs FileName:=localSaveLocation
xlsApp.Quit
On Error GoTo 0
DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel12, "DSRT_TEMP", localSaveLocation, True, "ERS"
numOfChangesDSRT = DCount("ID", "changed_records")
DoCmd.RunSQL "update ers_local inner join changed_records on changed_records.id = ers_local.id Set last_updated = Date();"
DoCmd.RunSQL "update ers_local inner join dsrt_temp on dsrt_temp.id = ers_local.id Set source = 'DSRT';"
DoCmd.RunSQL "DELETE FROM [dsrt_ers] WHERE dsrt_ers.id in (select id from ers_local where source = 'DSRT');"
DoCmd.RunSQL "INSERT INTO DSRT_ERS SELECT * FROM DSRT_TEMP"
DoCmd.RunSQL "DROP TABLE DSRT_TEMP;"
xlsApp.Quit
DoCmd.Requery
DoCmd.Hourglass False
Exit Sub
adminError:
DoCmd.Hourglass False
Exit Sub
saveError:
DoCmd.Hourglass False
On Error Resume Next
xlsApp.Quit
Exit Sub
End Sub

Be very careful opening and closing the Excel objects correctly:
Const localSaveLocation = ########
Const NetworkDSRTLocation = ########
Private Sub download_btn_Click()
Dim xlsApp As Excel.Application
Dim xlsBook As Excel.Workbook
Dim xlsSheet As Excel.Worksheet
Set xlsApp = New Excel.Application
Set xlsBook = xlsApp.Workbooks.Open(NetworkDSRTLocation)
' Go to the ERS tab of the workbook, delete the first 3 rows.
Set xlsSheet = xlsBook.Worksheets("ERS")
For row_ctr = 1 To 3
xlsSheet.Rows(1).EntireRow.Delete
Next row_ctr
' Set up 'ERS' named range for all cells in this worksheet.
xlsSheet.UsedRange.Select
col_count = xlsSheet.Cells(1, Columns.Count).end(xlToLeft).Column
row_count = (xlsSheet.Cells(Rows.Count, 1).end(xlUp).Row) + 1
xlsBook.Names.Add name:="ERS", RefersToR1C1:="=ERS!R1C1:R" & row_count & "C" & col_count
If Dir(localSaveLocation, vbNormal) <> "" Then
Kill localSaveLocation
End If
xlsBook.SaveAs FileName:=localSaveLocation
Set xlsSheet = Nothing
xlsBook.Close
Set xlsBook = Nothing
xlsApp.Quit
Set xlsApp = Nothing
DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel12, "DSRT_TEMP", localSaveLocation, True, "ERS"
numOfChangesDSRT = DCount("ID", "changed_records")
DoCmd.RunSQL "update ers_local inner join changed_records on changed_records.id = ers_local.id Set last_updated = Date();"
DoCmd.RunSQL "update ers_local inner join dsrt_temp on dsrt_temp.id = ers_local.id Set source = 'DSRT';"
DoCmd.RunSQL "DELETE FROM [dsrt_ers] WHERE dsrt_ers.id in (select id from ers_local where source = 'DSRT');"
DoCmd.RunSQL "INSERT INTO DSRT_ERS SELECT * FROM DSRT_TEMP"
DoCmd.RunSQL "DROP TABLE DSRT_TEMP;"
DoCmd.Requery
DoCmd.Hourglass False
End Sub

Related

Check if a query exists using VBA

I have a procedure in VBA which runs a report based on what user select on a dropdown (Report Name and grouping criteria) in a form. Also, I have a button which that user selection can be transferred to Excel.
The part of the procedure where I want to test if the query exist is below:
If Not IsNull(Me.cmbGroup.Value) Or Me.cmbGroup.Value = "" Then
strSQL = "SELECT * FROM qryCrossTotGroup WHERE [Group]='" & Me.cmbGroup.Value & "'"
'MsgBox strSQL
With MyDatabase
.QueryDefs.Delete ("tmpOutQry")
Set MyQueryDef = .CreateQueryDef("tmpOutQry", strSQL)
End With
'Step 3: Open the query
Set MyRecordset = MyDatabase.OpenRecordset(strSQL)
'Step 4: Clear previous contents
Dim xlApp As Object
Set xlApp = CreateObject("Excel.Application")
With xlApp
.Visible = True
.Workbooks.Add
.Sheets("Sheet1").Select
'Step 5: Copy the recordset to Excel
.ActiveSheet.Range("A2").CopyFromRecordset MyRecordset
'Step 6: Add column heading names to the spreadsheet
For i = 1 To MyRecordset.Fields.Count
xlApp.ActiveSheet.Cells(1, i).Value = MyRecordset.Fields(i - 1).Name
Next i
xlApp.Cells.EntireColumn.AutoFit
End With
End If
I want to test if query "tmpOutQry" exist in order to delete it. Have someone that line of code?
You don't use the temp query, so you can reduce your first steps to:
strSQL = "SELECT * FROM qryCrossTotGroup WHERE [Group]='" & Me.cmbGroup.Value & "'"
'MsgBox strSQL
'Step 3: Open the query
Set MyRecordset = MyDatabase.OpenRecordset(strSQL)
I tried this code and worked:
With MyDatabase
For Each MyQueryDef In CurrentDb.QueryDefs
If MyQueryDef.Name = "tmpOutQry" Then
.QueryDefs.Delete ("tmpOutQry")
Exit For
End If
Next
Set MyQueryDef = .CreateQueryDef("tmpOutQry", strSQL)
End With

Getting an error when trying to read all rows of a recordset

I created a query separately and now want to use VBA to read its records and then send certain fields of all rows in an email.
I am currently stuck on trying to extract all the rows from the recordset. I know how to do it for one record, but not with a dynamic recordset. Every week, the recordset could potentially have 1-10 (approx.) records. I had hoped to do this by dynamically reading all rows, saving the fields that I want into variables, and then adding that to the email body, but I arrived at an error.
I'm getting an error that says: Run-time error '3265': Item not found in this collection.
Does anyone know how to fix this error and how I can put all resulting rows of the recordset into the email body?
The code:
Private Sub Form_Timer()
'current_date variable instantiated in a module elsewhere
current_date = Date
'Using the Date function to run every Monday, regardless of the time of day
If current_date = (Date - (DatePart("w", Date, 2, 1) - 1)) Then
'MsgBox ("the current_date variable holds: " & current_date)
Dim dbs As DAO.Database
Dim rst As DAO.Recordset
Dim qdf As DAO.QueryDef
Dim prm As DAO.Parameter
Dim varRecords As Variant
Dim intNumReturned As Integer
Dim intNumColumns As Integer
Dim intColumn As Integer
Dim intRow As Integer
Dim strSQL As String
Dim rst_jobnumber As String
Dim rst_bfloc As String
Set dbs = CurrentDb
Set qdf = dbs.QueryDefs("qry_BMBFLoc")
Set rst = qdf.OpenRecordset
If rst.EOF Then
MsgBox "Null."
Else
'Found this part of the code online and not sure if I'm using it right.
varRecords = rst!GetRows(3)
intNumReturned = UBound(varRecords, 2) + 1
intNumColumns = UBound(varRecords, 1) + 1
For intRow = 0 To intNumReturned - 1
For intColumn = 0 To intNumColumns - 1
Debug.Print varRecords(intColumn, intRow)
Next intColumn
Next intRow
'End of code found online.
'rst.MoveFirst 'commenting this out because this query could potentially return multiple rows
rst_jobnumber = rst!job & "-" & rst!suffix
rst_bfloc = rst!Uf_BackflushLoc
rst.Close
dbs.Close
Set rst = Nothing
Set dbs = Nothing
'Dim oApp As Outlook.Application
'Dim oMail As MailItem
'Set oApp = CreateObject("Outlook.application")
'mail_body = "The following jobs do not have the special BF location set in Job Orders: " & rst_
'Set oMail = oApp.CreateItem(olMailItem)
'oMail.Body = mail_body
'oMail.Subject = "Blow Molding Jobs Missing BF Location"
'oMail.To = "something#something.com" 'in the future, create a function that finds all of the SC users' emails from their Windows user
'oMail.Send
'Set oMail = Nothing
'Set oApp = Nothing
End If
End If
ErrorHandler:
MsgBox "Error #: " & Err.Number & vbCrLf & vbCrLf & Err.Description
End Sub
Try working with this code and see how it works for you. I was unsure if you were sending one email per or one email listing all (I assumed the latter)
Dim dbs As DAO.Database
Dim rst As DAO.Recordset
Dim strMessageBody As String
Set dbs = CurrentDb
Set rst = CurrentDb.OpenRecordset("qry_BMBFLoc")
strMessageBody = "The following jobs do not have the special BF location set in Job Orders: "
If Not (rst.EOF And rst.BOF) Then
rst.MoveFirst
Do Until rst.EOF = True
strMessageBody = strMessageBody & rst!job & "-" & rst!suffix & ","
rst.MoveNext
Loop
If Right(strMessageBody, 1) = "," Then strMessageBody = Left(strMessageBody, Len(strMessageBody)-1)
End If
rst.Close
Set rst = Nothing
Set dbs = Nothing
EDIT - not using dot operator
Replace
varRecords = rst!GetRows(3)
with
varRecords = rst.GetRows(3)
Do you have three rows in your recordset?
If not rst!GetRows(3) will return false - and then next line will fail when you try to use UBound.
A good example of how to implement GetRows
Another possibility is if you're trying to access a Field that's not in your recordset on a line that has rst!

adding an on error code

I have a code in my form, once you type in an employees id it searches a table and auto fills the corresponding data. If the number is incomplete or is not in the table an error window pops up
And below is the code:
The Me.txtEmpName = rec("EMP_NA") is highlighted. I would like in the instance of an incomplete ID or if the ID does not exist, a msg box appears saying that employee id is not valid, please try again. or something along those lines, then it just goes back to the form instead of getting the error message above. Any thoughts?
Private Sub txtEmpID_AfterUpdate()
Dim db As DAO.Database
Dim rec As DAO.Recordset
Set db = CurrentDb
strSQL = "Select * From tblEmpData Where TMSID = '" & Me.txtEmpID & "'"
Set rec = db.OpenRecordset(strSQL)
Me.txtEmpName = rec("EMP_NA")
Me.cboGender = rec("EMP_SEX_TYP_CD")
Me.cboEEOC = rec("EMP_EOC_GRP_TYP_CD")
Me.txtDivision = rec("DIV_NR")
Me.txtCenter = rec("CTR_NR")
Me.cboRR = rec("REG_NR")
Me.cboDD = rec("DIS_NR")
Me.txtJobD = rec("JOB_CLS_CD_DSC_TE")
Me.cboJobGroupCode = rec("JOB_GRP_CD")
Me.cboFunction = rec("JOB_FUNCTION")
Me.cboMtgReadyLvl = rec("Meeting_Readiness_Rating")
Me.cboMgrReadyLvl = rec("Manager_Readiness_Rating")
Me.cboJobGroup = rec("JOB_GROUP")
Set rec = Nothing
Set db = Nothing
End Sub
After you open the recordset, check whether it is empty. If empty, present your message. If not empty, load the recordset values into your data controls.
Set rec = db.OpenRecordset(strSQL)
If (rec.BOF And rec.EOF) Then
' when both BOF and EOF are True, the recordset is empty
MsgBox "employee id is not valid, please try again"
Else
Me.txtEmpName = rec("EMP_NA")
' and so forth
End If
The following code will fix the issue you are having. 1. You are not finding a record, thus the error you received. 2. If you want to handle other errors, change the code in the Error_Trap to test for the error number.
Private Sub txtEmpID_AfterUpdate()
Dim db As DAO.Database
Dim rec As DAO.Recordset
On Error GoTo Error_Trap
Set db = CurrentDb
strSQL = "Select * From tblEmpData Where TMSID = '" & Me.txtEmpID & "'"
Set rec = db.OpenRecordset(strSQL)
If rec.EOF Then
MsgBox "The Employee ID you entered was not found. Please try again", vbOKOnly, "Wrong ID"
GoTo Close_It
End If
Me.txtEmpName = rec("EMP_NA")
Me.cboGender = rec("EMP_SEX_TYP_CD")
Me.cboEEOC = rec("EMP_EOC_GRP_TYP_CD")
Me.txtDivision = rec("DIV_NR")
Me.txtCenter = rec("CTR_NR")
Me.cboRR = rec("REG_NR")
Me.cboDD = rec("DIS_NR")
Me.txtJobD = rec("JOB_CLS_CD_DSC_TE")
Me.cboJobGroupCode = rec("JOB_GRP_CD")
Me.cboFunction = rec("JOB_FUNCTION")
Me.cboMtgReadyLvl = rec("Meeting_Readiness_Rating")
Me.cboMgrReadyLvl = rec("Manager_Readiness_Rating")
Me.cboJobGroup = rec("JOB_GROUP")
Close_It:
Set rec = Nothing
Set db = Nothing
Exit Sub
Error_Trap:
If Err.Number = 99999999 Then ' Change this line to test for other conditions
MsgBox "...... ", vbOKOnly, "....."
Resume Close_It
End If
End Sub

In VBA, how does one make a table from a recordset

I have a recordset that I want to export into Excel 2000 format (acSpreadsheetTypeExcel9). I believe I need to drop it into a table first then execute a DoCmd.TransferSpreadsheet (keeps it easy and it works). The user sets just a few parameters in the form, thus the Me. syntax you will see.
Here's the working code so far:
Select Case Me.Controls("frame_ChooseReport").Value
Case 1
sExecuteQuery = "qry_PDSR w/ Destruct Dates"
bHasProgramCode = True
sFileName = "Project_Doc_Submittal_Request_better"
Case 2
sExecuteQuery = "qry_PDSR w/Destruct Dates BE"
bHasProgramCode = False 'This is the only query here that doesn't have a Program Code parameter
sFileName = "Project_Doc_Submittal_Request_better_BE"
Case 3
sExecuteQuery = "qry_Project Documentation Submittal Request w/ Destruct Dates"
bHasProgramCode = True
sFileName = "Project_Doc_Submittal_Request_ENH"
Case 4
sExecuteQuery = "qry_Project_Doc_Submittal_Request_w_Destruct_Dates_HES_Installer"
bHasProgramCode = True
sFileName = "Project_Doc_Submittal_Request_Installer"
Case Else
Stop 'Error! This should never be reached!
End Select
'Execute query & save output to Excel
Set qdf = CurrentDb.QueryDefs(sExecuteQuery) 'Open the query
'Assign values to the query using the parameters option
If bHasProgramCode = True Then
qdf.Parameters(0) = Me.lbl_ProgramCodes.Section
qdf.Parameters(1) = Me.txt_StartDate
qdf.Parameters(2) = Me.txt_EndDate
Else
qdf.Parameters(0) = Me.txt_StartDate
qdf.Parameters(1) = Me.txt_EndDate
End If
sFullPath = Me.lbl_SaveTo.Caption & "\" & sFileName
Set rst = qdf.OpenRecordset 'Convert the querydef to a recordset and run it
If rst.BOF = True And rst.EOF = True Then
MsgBox "No records were found.", vbExclamation, "Empty recordset"
Exit Sub
End If
'Dump recordset into a table, export it to Excel, then delete it.
'Here is where the recordset needs to become a table.
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel9, "qry_PDSR w/ Destruct Dates", sFullPath, True 'Export table to an Excel format
'Clean up!
DoCmd.DeleteObject acTable, gTEMP_TBL 'Done with the temporary table so delete it
rst.Close
qdf.Close
Set rst = Nothing
Set qdf = Nothing
Help/suggestions? Thank you.
Access 2010 in Windows 7
---------- FOLLOW UP ----------
Here's the query I added that will use the references of the opened form per Remou's suggestion:
SELECT dbo_PROJECT.PROJECTID, dbo_PROJECT.TITLE, dbo_PROJECT.PROGRAMCODE, dbo_PROJECT.PROJECTTYPE, dbo_PROJECT.REFERENCE, dbo_PROJECT.STATUS, dbo_PROJECT.PMC, dbo_TRANSACTION_SUM.STATUS, dbo_TRANSACTION_SUM.IMPORTEDDT, dbo_TRANSACTION_SUM.CHECKDT, dbo_PROJECT.NOTES, dbo_TRANSACTION_SUM.TRANSACTIONID, dbo_TRANSACTION_SUM.GL_ACCT, dbo_PROJECT_SUM.PAID_INCENT_TOTAL, dbo_TRANSACTION_SUM.AMOUNT
FROM ((dbo_INCENTIVE RIGHT JOIN dbo_PROJECT ON dbo_INCENTIVE.PROJECTID = dbo_PROJECT.PROJECTID) LEFT JOIN dbo_TRANSACTION_SUM ON dbo_INCENTIVE.INCENTIVEID = dbo_TRANSACTION_SUM.INCENTIVEID) LEFT JOIN dbo_PROJECT_SUM ON dbo_PROJECT.PROJECTID = dbo_PROJECT_SUM.PROJECTID
WHERE (((dbo_PROJECT.PROGRAMCODE) In ([Forms]![Submittal_Request_Report]![txt_ListProgramCodeSelections])) AND ((dbo_TRANSACTION_SUM.CHECKDT) Between [Forms]![Submittal_Request_Report]![txt_StartDate] And [Forms]![Submittal_Request_Report]![txt_EndDate]));
Here's the routine that is in the On_Exit event of the listbox:
Private Sub list_ProgramCodes_Exit(Cancel As Integer)
'Get selection from Program Code listbox and store it in a hidden textbox for the query.
Dim x As Long, sValue As String, ctlSource As Control
sValue = ""
Set ctlSource = Me!list_ProgramCodes
For x = 0 To ctlSource.ListCount - 1
If ctlSource.Selected(x) Then
sValue = sValue & ctlSource.Column(0, x) & ","
End If
Next
Me.txt_ListProgramCodeSelections.Value = Left(sValue, Len(sValue) - 1) 'Drop the last comma
Set ctlSource = Nothing
End Sub
Works great! The SQL line In ([Forms]![Submittal_Request_Report]![txt_ListProgramCodeSelections]) pulls the list of items in the hidden textbox (using a label didn't work) that was populated with the selection from the listbox on the form.
This is now the code for exporting the query:
Private Sub btn_RunReport_Click()
Dim sExecuteQuery As String, sFullPath As String, sFileName As String
On Error GoTo Err_btn_RunReport_Click
If Left(Me.lbl_SaveTo.Caption, 4) = "save" Then
MsgBox "Please select a folder to save the results to.", vbInformation, "No folder selected"
Exit Sub
End If
Select Case Me.Controls("frame_ChooseReport").Value
Case 1
sExecuteQuery = "qry_PDSR_Destruct_Dates_form"
sFileName = "Project_Doc_Submittal_Request.xls"
Case 2
sExecuteQuery = "qry_Project_Doc_Submittal Request w/ Destruct Dates_form"
sFileName = "Project_Doc_Submittal_Request_ENH.xls"
Case 3
sExecuteQuery = "qry_PDSR_w_Destruct_Dates_HES_Installer_form"
sFileName = "Project_Doc_Submittal_Request_Installer.xls"
Case Else
Stop 'Error! This should never be reached!
End Select
sFullPath = Me.lbl_SaveTo.Caption & "\" & sFileName
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel9, sExecuteQuery, sFullPath, True 'Export table to an Excel format
Exit_btn_RunReport_Click:
Exit Sub
Err_btn_RunReport_Click:
MsgBox Err.Description
Resume Exit_btn_RunReport_Click
End Sub
Thanks Remou!
I suggest you just set the sql of a query to a suitable string and then export the query:
sSQL="SELECT * FROM Table WHERE Field=" & me.MyText
If IsNull(DLookup("name", "msysobjects", "name='query1'")) Then
CurrentDb.CreateQueryDef "Query1", sSQL
Else
CurrentDB.QueryDefs("Query1").SQL = sSQL
End If
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel9, "Query1", sFullPath
You can create a query that references an open form:
SELECT Test.ID, Test.Data
FROM Test
WHERE Test.AField=[forms]![test]![pickone]

How to copy a linked table to a local table in Ms Access programmatically?

So I'd like to copy a linked table to a local one in code, structure and data in MS Access 2003.
Code being : VBA or C#. Or anything else for that matter..
UPDATE : I want the copy structure and data behaviour from ms access to keep the Primary Keys. If you copy a linked table, you can choose to paste it as 'structure and data (local table)'
It is that I want to achieve in code.
My understanding is that DAO does not support the decimal data type, but ADOX does. Here's an updated procedure that uses ADOX instead to copy the schema to a new table.
One interesting item of note: The OLEDB provider for Jet sorts the columns alphabetically rather than by ordinal position as explained in this KB article. I wasn't concerned about preserving the ordinal position, but you may be, in which case you can update this procedure to meet your needs.
In order for the ADOX version of the code to work, you'll need to set a reference to Microsoft ADO Ext. 2.x for DDL and Security (where x = version number; I used 2.8 to test this procedure). You'll also need a reference to ADO as well.
Public Sub CopySchemaAndData_ADOX(ByVal sourceTableName As String, ByVal destinationTableName As String)
On Error GoTo Err_Handler
Dim cn As ADODB.Connection
Dim cat As ADOX.Catalog
Dim sourceTable As ADOX.Table
Dim destinationTable As ADOX.Table
Set cn = CurrentProject.Connection
Set cat = New ADOX.Catalog
Set cat.ActiveConnection = cn
Set destinationTable = New ADOX.Table
destinationTable.Name = destinationTableName
Set sourceTable = cat.Tables(sourceTableName)
Dim col As ADOX.Column
For Each col In sourceTable.Columns
Dim newCol As ADOX.Column
Set newCol = New ADOX.Column
With newCol
.Name = col.Name
.Attributes = col.Attributes
.DefinedSize = col.DefinedSize
.NumericScale = col.NumericScale
.Precision = col.Precision
.Type = col.Type
End With
destinationTable.Columns.Append newCol
Next col
Dim key As ADOX.key
Dim newKey As ADOX.key
Dim KeyCol As ADOX.Column
Dim newKeyCol As ADOX.Column
For Each key In sourceTable.Keys
Set newKey = New ADOX.key
newKey.Name = key.Name
For Each KeyCol In key.Columns
Set newKeyCol = destinationTable.Columns(KeyCol.Name)
newKey.Columns.Append (newKeyCol)
Next KeyCol
destinationTable.Keys.Append newKey
Next key
cat.Tables.Append destinationTable
'Finally, copy data from source to destination table
Dim sql As String
sql = "INSERT INTO " & destinationTableName & " SELECT * FROM " & sourceTableName
CurrentDb.Execute sql
Err_Handler:
Set cat = Nothing
Set key = Nothing
Set col = Nothing
Set sourceTable = Nothing
Set destinationTable = Nothing
Set cn = Nothing
If Err.Number <> 0 Then
MsgBox Err.Number & ": " & Err.Description, vbCritical, Err.Source
End If
End Sub
Here's the original DAO procedure
Public Sub CopySchemaAndData_DAO(SourceTable As String, DestinationTable As String)
On Error GoTo Err_Handler
Dim tblSource As DAO.TableDef
Dim fld As DAO.Field
Dim db As DAO.Database
Set db = CurrentDb
Set tblSource = db.TableDefs(SourceTable)
Dim tblDest As DAO.TableDef
Set tblDest = db.CreateTableDef(DestinationTable)
'Iterate over source table fields and add to new table
For Each fld In tblSource.Fields
Dim destField As DAO.Field
Set destField = tblDest.CreateField(fld.Name, fld.Type, fld.Size)
If fld.Type = 10 Then
'text, allow zero length
destField.AllowZeroLength = True
End If
tblDest.Fields.Append destField
Next fld
'Handle Indexes
Dim idx As Index
Dim iIndex As Integer
For iIndex = 0 To tblSource.Indexes.Count - 1
Set idx = tblSource.Indexes(iIndex)
Dim newIndex As Index
Set newIndex = tblDest.CreateIndex(idx.Name)
With newIndex
.Unique = idx.Unique
.Primary = idx.Primary
'Some Indexes are made up of more than one field
Dim iIdxFldCount As Integer
For iIdxFldCount = 0 To idx.Fields.Count - 1
.Fields.Append .CreateField(idx.Fields(iIdxFldCount).Name)
Next iIdxFldCount
End With
tblDest.Indexes.Append newIndex
Next iIndex
db.TableDefs.Append tblDest
'Finally, copy data from source to destination table
Dim sql As String
sql = "INSERT INTO " & DestinationTable & " SELECT * FROM " & SourceTable
db.Execute sql
Err_Handler:
Set fld = Nothing
Set destField = Nothing
Set tblDest = Nothing
Set tblSource = Nothing
Set db = Nothing
If Err.Number <> 0 Then
MsgBox Err.Number & ": " & Err.Description, vbCritical, Err.Source
End If
End Sub
try docmd.CopyObject or docmd.TransferDatabase
Create this query and execute it
SELECT * INTO MyNewTable FROM LinkedTableName
In VBA you can do
docmd.runsql "SELECT * INTO MyNewTable FROM LinkedTableName"
To keep the structure you would have to do DoCmd.TransferDatabase acImport