I have been looking for a solution to inserting data into excel using vb6 code and access database. There are many cases where I need to write to an excel spreadsheet multiple times with different records of data. I have an existing workbook that I am trying to open and "save as" when I am complete. I have been able to open the excel workbook, access the sheet I am writing to, and the cells I am writing to, however I can only write to the workbook once and when I leave the scope of the open workbook the connection is closed.
I have a sub routine that creates the workbook object, opens the existing workbook and work sheet, writes to a specified cell number to insert the new data. I have looked at official support pages and it doesn't seem to have what I am looking for at this time.
Am I making this too complicated or is there a solution for this? Please help.
My current code:
Row Arrays
Private oldDataRowArray(3 To 21) As Integer
Private updatedDataRowArray(5 To 2) As Integer
Loop logic
Dim i As Integer
Dim n As Integer
i = 3
n = 5
Do While i <= UBound(oldDataRowArray) And n <= UBound(updatedDataRowArray)
EditExcelSheet txtWorkbookFileName.Text, i, n //sub routine
i = i + 3 //skip number of cells
n = n + 3 //skip number of cells
Loop
Sub Routine to Insert data into Excel
Private Sub EditStakingSheet(ByVal workbook As String, ByVal oldDataRowIndex As Integer, ByVal newDataRowIndex As Integer)
Dim objExcel As Object
Dim objWorkBook As Object
Dim objSheet As Object
Set objExcel = New Excel.Application
Set objWorkBook = objExcel.Workbooks.Open(workbook)
Set objSheet = objWorkBook.Worksheets(1)
objExcel.Visible = True
//insert old value
objSheet.Cells(oldDataRowIndex , 26).Value = "old Value"
//insert new value
objSheet.Cells(newDataRowIndex , 26).Value = "new Value"
End Sub
You could use adodb objects.
This video is a good tutorial for this.
Here is an example how you can use adodb. You need to install the activeX Data Objects Libary for this.
For .source= you can use any sql-query.
Public Function get_Value(table As String, order_by As String) As Variant
Set db_data = New ADODB.Recordset
Set db1 = New ADODB.Connection
pDB_path = "#enter db-path here"
db1.ConnectionString = _
"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & pDB_path & ";Persist Security Info=False;"
db1.Open
With db_data
.ActiveConnection = db1
.Source = "SELECT * FROM " & table & " ORDER BY " & order_by & " ASC"
.LockType = adLockReadOnly 'read only access to db
.CursorType = adOpenStatic 'how to update the database
.Open
End With
get_Value = TransposeArray(db_data.GetRows)
db_data.Close
End Function
Related
I have a data connection in my xlsm file, which is called "DATA".
I created my combo box and input the value from a range.
Now I need to return a result set based on the value from the combo box (drop down list). e.g. if the value in the dropdown list is "CompanyXYZ", then my query from "DATA" needs to be returned but only the data for CompanyXYZ.
The sql equivalent is:
"SELECT * FROM [query] where [column] = combobox
Issue #1
Below is my sheet("DATA"). It has a table returned by the SQL query. One of the columns is Debtor_Name. It has more than 8500 rows but only 90 are unique.
In my other sheet, I have an ActiveX ComboBox that needs to return all the unique values from DATA.Debtor_name column (the 90 unique values).
Sample VBA for issue #1:
Sub Populate_Combobox_Worksheet()
'The Excel workbook and worksheets that contain the data, as well as the range placed on that data
Dim wbBook As Workbook
Dim wsSheet As Worksheet
Dim rnData As Range
'Variant to contain the data to be placed in the combo box.
Dim vaData As Variant
'Initialize the Excel objects
Set wbBook = ThisWorkbook
Set wsSheet = wbBook.Worksheets("DATA")
'Set the range equal to the data, and then (temporarily) copy the unique values of that data to the L column.
With wsSheet
Set rnData = .Range(.Range("D1"), .Range("D10000").End(xlUp))
rnData.AdvancedFilter Action:=xlFilterCopy, _
CopyToRange:=.Range("X1"), _
Unique:=True
'store the unique values in vaData
vaData = .Range(.Range("X2"), .Range("X10000").End(xlUp)).Value
'clean up the contents of the temporary data storage
.Range(.Range("X1"), .Range("X10000").End(xlUp)).ClearContents
End With
'display the unique values in vaData in the combo box already in existence on the worksheet.
With wsSheet.OLEObjects("ComboBox1").Object
.Clear
.List = vaData
.ListIndex = -1
End With
End Sub
Issue #2.
Now the end user will need to select a debtor_name from the combo box, then click on refresh data. This DATA REFRESH will need to only pull the data from SQL where debtor_name = [selected value in combo box]
I asked about for issue #2 because I did not know I had an issue with my combo box (issue #1); however, I can handle that somehow; only need help with issue #2 now.
You can use SQL to populate the ComboBox with unique values.
Option Explicit
Sub Populate_Combobox_Worksheet()
Dim con As ADODB.Connection, rs As ADODB.Recordset, SQL As String
Set con = GetConnection
' query
SQL = " SELECT DISTINCT [Debtor_name] FROM [DATA$]" & _
" WHERE [Debtor_name] IS NOT NULL" & _
" ORDER BY [Debtor_Name]"
Set rs = con.Execute(SQL)
With Sheet2.ComboBox1
.Clear
.List = Application.Transpose(rs.GetRows)
.ListIndex = -1
End With
con.Close
End Sub
Sub RefreshData()
Dim con As ADODB.Connection, rs As ADODB.Recordset, SQL As String
Set con = GetConnection
' query
SQL = " SELECT * FROM [DATA$]" & _
" WHERE [Debtor_name] = '" & Sheet2.ComboBox1.Value & "'"
Set rs = con.Execute(SQL)
Sheet2.Range("A1").CopyFromRecordset rs
con.Close
End Sub
Function GetConnection() As ADODB.Connection
Dim wb As Workbook, sCon As String
Set wb = ThisWorkbook
sCon = "Data Source=" & wb.FullName & "; " & _
"Extended Properties=""Excel 12.0;HDR=Yes;"";"
' connect
Set GetConnection = New ADODB.Connection
With GetConnection
.Provider = "Microsoft.ACE.OLEDB.12.0;"
.ConnectionString = sCon
.Open
End With
End Function
I am using the code from Function to export query or table to MS Excel to export all the data from one Access table to a worksheet in MS Excel.
This program stores time in and time out of employees in the table.
Let's say the admin wants to filter the data from 01 Jan 19 to 15 Jan 19.
I want to put two datepickers on my form as a basis for the "From" and "To".
I want to export that selected data. How can I inject that to this code?
Public Function Export2XL(InitRow As Long, DBAccess As String, DBTable As String) As Long
Dim cn As New ADODB.Connection 'Use for the connection string
Dim cmd As New ADODB.Command 'Use for the command for the DB
Dim rs2 As New ADODB.Recordset 'Recordset return from the DB
Dim MyIndex As Integer 'Used for Index
Dim MyRecordCount As Long 'Store the number of record on the table
Dim MyFieldCount As Integer 'Store the number of fields or column
Dim ApExcel As Object 'To open Excel
Dim MyCol As String
Dim Response As Integer
Set ApExcel = CreateObject("Excel.application") 'Creates an object
ApExcel.Visible = True 'This enable you to see the process in Excel
pExcel.Workbooks.Add 'Adds a new book.
ApExcel.ActiveSheet.Name = "" & (Export_data.Label1.Caption) & ""
'Set the connection string
cn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;data source=" &
app.Path & "\Dbase.mdb; User ID=admin;Persist Security Info=False;JET
OLEDB:Database Password=akgtrxx21"
'Open the connection
cn.Open
'Check that the connection is open
If cn.State = 0 Then cn.Open
Set cmd.ActiveConnection = cn
cmd.CommandText = DBTable
cmd.CommandType = adCmdTable
Set rs2 = cmd.Execute
'Count the number of fields or column
MyFieldCount = rs2.Fields.count
'Fill the first line with the name of the fields
For MyIndex = 0 To MyFieldCount - 1
ApExcel.Cells(InitRow, (MyIndex + 1)).Formula = rs2.Fields(MyIndex).Name
'Write Title to a Cell
ApExcel.Cells(InitRow, (MyIndex + 1)).Font.Bold = True
ApExcel.Cells(InitRow, (MyIndex + 1)).Interior.ColorIndex = 36
ApExcel.Cells(InitRow, (MyIndex + 1)).WrapText = True
Next
'Draw border on the title line
MyCol = Chr((64 + MyIndex)) & InitRow
ApExcel.Range("A" & InitRow & ":" & MyCol).Borders.Color = RGB(0, 0, 0)
MyRecordCount = 1 + InitRow
'Fill the excel book with the values from the database
Do While rs2.EOF = False
For MyIndex = 1 To MyFieldCount
ApExcel.Cells(MyRecordCount, MyIndex).Formula = rs2((MyIndex - 1)).Value
'Write Value to a Cell
ApExcel.Cells(MyRecordCount, MyIndex).WrapText = False 'Format the Cell
Next
MyRecordCount = MyRecordCount + 1
rs2.MoveNext
If MyRecordCount > 50 Then
Exit Do
End If
Loop
'Close the connection with the DB
rs2.Close
'Return the last position in the workbook
Export2XL = MyRecordCount
Set cn = Nothing
Set cmd = Nothing
Set rs2 = Nothing
Set ApExcel = Nothing
End Function
Excel does have a way to import data from Access with no VBA at all.
Create the connection to fill your worksheet.
Go to Menu Data > Access.
You will be asked to pick an Access database and select the table you want. You probably want a query to be executed but for now, pick any table; this will be edited later.
Edit the query to what you want.
Open the connection window by clicking on the menu Data > Connections and pick the connection you have just created. Then, go to the next tab (Definition), change Command Type from Table to SQL then in command text, type your command.
Don't close the window just yet.
Add condition on your date.
If the field is called, for instance, MyDate, then add a WHERE clause like this one: (MyDate >= ? AND MyDate <= ?).
When you refresh the data, you will be prompted to give values to replace the 2 question marks, and you will have the option to designate a cell to do it. You will also have an option for the query to always use what you have defined.
Note that when done correctly, you can reorder fields and/or create formulae in the table without causing any sort of problem to Excel at all. You can also create a Total row at the bottom to sum up values, using a formula (Excel will show you a dropdown to create a SUBTOTAL formula, that is conveniently sensitive to filters.
If you want to refresh data with VBA, it takes a single line of code to do: ThisWorkbook.Connections(...).Refresh or ApExcel.Workbooks(..).Connections(...).Refresh.
PS: If you absolutely want to keep your code above, then at least make sure not to copy rs2 cell by cell (that is way to slow due to Excel event handling) but rather, do something like: ApExcel.Cells(2, 1).CopyFromRecordset rs2
I have been performing these operations through excel, but the raw data that I am dealing with is overwhelming for excel. I shifted over to access as the database but I am trying to find similar functionality. I am still using excel as my interface.
I am essintially breaking my data into samples. Taking a sample of the data, analyzing it then moving on to the next sample. I would like to do the same with the Access data. What I am looking for is how to perform operations (summation, multipication, division,max, min...) on a sample using a for loop. Also, what is the ID of max value and ID on the min value. I am also dealing with a huge amout of raw data, so the less lines of code would be optimal. Any pointers in the right direction would be appreciated.
Code in Excel:
For CoUnTer = 1 To FindRecordCount
Set Circ_Rng = WS.Range(WS.Cells(CoUnTer , 5), WS.Cells(CoUnTer + TempTimeFrame - 1, 5))
Set DataPoint_Rng = WS.Range(WS.Cells(CoUnTer , 1), WS.Cells(CoUnTer + TempTimeFrame - 1, 1))
Set DataPoint_Circ_Rng = WS.Range(WS.Cells(CoUnTer , 8), WS.Cells(CoUnTer + TempTimeFrame - 1, 8))
Set DataPoint_SQ_Rng = WS.Range(WS.Cells(CoUnTer , 9), WS.Cells(CoUnTer + TempTimeFrame - 1, 9))
'---------------------------
MaxPoint = WorksheetFunction.Max(CircPressure_Rng)
Row_At_MaxPressure = WS.Columns(5).Cells.Find(MaxPoint, After:=WS.Cells(3, 5), SearchOrder:=xlByRows, LookIn:=xlValues, SearchDirection:=xlNext).Row
Sigma_X_Sigma_Y = WorksheetFunction.Sum(Circ_Rng) * WorksheetFunction.Sum(DataPoint_Rng)
Sigma_XY = WorksheetFunction.Sum(DataPoint_Circ_Rng)
Sigma_X2 = WorksheetFunction.Sum(DataPoint_SQ_Rng)
Min_X = WorksheetFunction.Min(DataPoint_Rng)
Code for Access data:
This code gets the data sample. What I am missing how can I handle the operations on them similar to what I have done in excel.
Sub GetAccessData()
Dim DBFullName As String
Dim Connect As String, Source As String
Dim Connection As ADODB.Connection
Dim Recordset As ADODB.Recordset
Dim Col As Long
Dim x As Long
Application.ScreenUpdating = False
'DataBase Path
DBFullName = WB_Path & "\RawData - Template.accdb"
'Open the Connection
Set Connection = New ADODB.Connection
Connect = "Provider=Microsoft.ACE.OLEDB.12.0;"
Connect = Connect & "Data Source=" & DBFullName & ";"
Connection.Open ConnectionString:=Connect
'Create a RecordSet
Set Recordset = New ADODB.Recordset
' Client-side cursor
Recordset.CursorLocation = adUseClient
With Recordset
Source = "SELECT * FROM RawData WHERE [ID] BETWEEN " & StartofData & " AND " & EndofData
.Open Source:=Source, ActiveConnection:=Connection
On Error Resume Next
End With
EndofData = Recordset.RecordCount
Set Recordset = Nothing
Connection.Close
Set Connection = Nothing
'Application.ScreenUpdating = True
'Application.EnableEvents = True
End Sub
As you are seeing, Excel is better for some things and Access is better for some things. Sometimes you can literally substitute Access for Excel, and vice versa, but often each one should be used for it's intended purpose. There are probably a few ways forward here, but I would lean towards leaving your Excel stuff in Excel, and control Excel from Access. You can use Late Binding or Early Binding to control one app from another app. Here are two options for you to consider.
‘EARLY BINDING
Option Compare Database
Option Explicit ' Use this to make sure your variables are defined
' One way to be able to use these objects throughout the Module is to Declare them
' Here and not in a Sub
Private objExcel As Excel.Application
Private xlWB As Excel.Workbook
Private xlWS As Excel.Worksheet
Sub Rep()
Dim strFile As String
strFile = "C:\your_path_here\YourExcelFile.xls"
' Opens Excel and makes it Visible
Set objExcel = New Excel.Application
objExcel.Visible = True
'Opens up the Workbook
Set xlWB = objExcel.Workbooks.Open(strFile)
'Sets the Workseet to the last active sheet - Better to use the commented version and use the name of the sheet.
Set xlWS = xlWB.ActiveSheet
'Set xlWS = xlWB("Sheet2")
With xlWS ' You are now working with the Named file and the named worksheet
End With
'Do Close and Cleanup
End Sub
‘LATE BINDING
Sub ControlExcelFromAccess()
' No reference to a type library is needed to use late binding.
' As long as the object supports IDispatch, the method can
' be dynamically located and invoked at run-time.
' Declare the object as a late-bound object
Dim oExcel As Object
Dim strFile As String
strFile = "C:\your_path_here\YourExcelFile.xls"
Set oExcel = CreateObject("Excel.Application")
' The Visible property is called via IDispatch
oExcel.Visible = True
Set xlWB = oExcel.Workbooks.Open(strFile)
'Call code here . . .
Set oExcel = Nothing
End Sub
Just copy/paste your code inside this code, make a few small tweaks if necessary, and you should be good to go!
Right now I am trying to export data from Excel to Access with VBA.
rst("2k2").Value = sProduct
This is an example of what I currently use which is putting the value sProduct into the column in Access called "2k2". How would I move to the next column in Access and put a value in that column? Besides the obvious using the name of that column.
Thanks!
You could set the fields to a variable and cycle through them.
Dim fld as DAO.Field
for each fld in rst.fields
'Do some stuff
next
Disclaimer: As mentioned in the comments, do not use this code in a production environment. Use it only for a DB, which you are using for a school project or anything similar. Databases are really different than an Excel Spreadsheet and the position of the columns is not always constant.
Having said this:
Option Explicit
Public Function GetColumnNameAfter(columnName As String) As String
Dim cnLogs As New ADODB.Connection
Dim rsHeaders As New ADODB.Recordset
Dim rsData As New ADODB.Recordset
Dim myCounter As Long
Dim myConn As String
Dim nameFound As Boolean
myConn = "Provider=SQLNCLI11;Server=(localdb)\MSSQLLocalDB;"
myConn = myConn & "Initial Catalog=Blog;Trusted_Connection=yes;timeout=30;"
cnLogs.Open myConn
With rsHeaders
.ActiveConnection = cnLogs
.Open "SELECT * FROM syscolumns WHERE id=OBJECT_ID('Posts')"
Do While Not rsHeaders.EOF
'Printing is fun...
'Debug.Print rsHeaders(0)
If nameFound Then
GetColumnNameAfter = rsHeaders(0)
Exit Function
End If
If columnName = rsHeaders(0) Then nameFound = True
myCounter = myCounter + 1
rsHeaders.MoveNext
Loop
.Close
End With
End Function
In the strConn change the Initial Catalog to your DB and the DB Table Posts here OBJECT_ID('Posts')" to your DB table. Then the function will return the String name of the column after the the one you have provided as a parameter.
The code loops through the Headers of a table, named "Posts", which is in a DB named Blog. Once it finds a header, named columnName, it sets the nameFound to True and returns the Name of the next column.
Once you know the name of the "Next" column, you can use the same logic as in your question.
I'm trying to take an ADO Recordset and then loop through it to parse individual rows to different worksheets within an Excel workbook. Unfortunately, as I step through my code, I get the following error: Run-time error '13': Type mismatch. This occurs when I call the sub in my code - it never actually steps into the sub. I'm wondering if I'm somehow not passing the Recordset in correctly or if it's a problem somewhere within my loop.
Regardless, here's my code - any help is greatly appreciated!
Sub SplitData(ByVal rs As ADODB.Recordset)
' Instantiate count variables for each result type
' Start at 2 to give room for Table headers on sheets
Dim NewAppsCount, BadLogCount, MatNotesCount, ZeroBalCount As Integer
NewAppsCount , BadLogCount, MatNotesCount, ZeroBalCount = 2
' Row Counter
Dim Count As Long
Count = 0
' Loop through the recordset and parse rows to appropriate worksheets
Do While Not rs.EOF
If CStr(rs.Fields("Maturity Date")) = "" Then
If CStr(rs.Fields("Log_Date")) = "" Then
' Applications that have not been properly logged
Sheet4.Range("A" & CStr(BadLogCount)) = rs.Fields(Count).Value
Count = Count + 1
BadLogCount = BadLogCount + 1
Else
' New Applications
Sheet6.Range("A" & CStr(NewAppsCount)) = rs.Fields(Count).Value
Count = Count + 1
NewAppsCount = NewAppsCount + 1
End If
Else
If Month(rs.Fields("Maturity Date")) < Month(Date) Then
' Maturing Notes with Zero Outstanding Balance
Sheet7.Range("A" & CStr(ZeroBalCount)) = rs.Fields(Count).Value
Count = Count + 1
ZeroBalCount = ZeroBalCount + 1
Else
' Maturing Notes
Sheet8.Range("A" & CStr(MatNotesCount)) = rs.Fields(Count).Value
Count = Count + 1
MatNotesCount = MatNotesCount + 1
End If
End If
rs.MoveNext
Loop
End Sub
Here is the sub that call GetData:
Sub GetData(ByVal Update As Boolean)
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim query As String
Dim path As String
Dim prompt, result As Integer
Dim day, today As String
' ...skipping stuff not related to the issue...
' Set the UNC Path
path = "\\this\is\the\path"
' Instantiate ADO Objects
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
' Connect to data source
conn.Open "Provider=Microsost.JET.OLEDB.4.0;Data Source=" & path & ";"
' The Query
query = "This is a big 'ol query that I won't repost here"
'Run the query and populate the Recordset object
rs.CursorLocation = adUseClient
rs.Open query, conn, adOpenStatic, adLockReadOnly
'Parse contetns of Recordset to worksheet
Application.ScreenUpdating = False
Me.SplitData(rs)
'Close the ADO Objects, set them to null, and exit sub
rs.Close
conn.Close
Set rs = Nothing
Set conn = Nothing
Exit Sub
End Sub
Try changing:
Me.SplitData(rs)
to:
Me.SplitData rs
Unnecessary parentheses often cause problems in VBA.
(NB I'm assuming that the two Sub shown are in a context where Me makes sense - e.g. class module, ThisWorkbook module, worksheet module, backing a UserForm etc)