Fill Listbox with Results of SQL Query - sql

Alright, I might be way off if what I've tried, but here goes. I am trying to execute a SQL query to return some records and then populate a listbox. Essentially, I want every record to display in a separate line. I have this in a separate module referencing the main form. I have heard some things about filling a datatable with the SQL results and then linking the listbox to that as well, but I'm not 100% sure what to do there either. I've just been trying to manually update the listbox with each result, but it doesn't seem to like it. I am using a recordset instead of a datatable, but like I said, it's because I'm unsure of how to use a datatable (haven't done it before but I am willing to learn)
Public Sub addCases()
'Uses windows login credentials to determine and return CSP's Manager's Name
'C
Dim i As Integer
Dim intX As Integer
Dim c As ADODB.Connection
Dim r As ADODB.Recordset
Dim strSQL As String, strManager As String
Set c = New ADODB.Connection
c.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Commit Tracker.accdb; Persist Security Info=False;"
strSQL = "SELECT "
strSQL = strSQL & "* "
strSQL = strSQL & "FROM "
strSQL = strSQL & "CommitTrk "
strSQL = strSQL & "WHERE "
strSQL = strSQL & "EMP_ID = '" & Username() & "'"
Set r = c.Execute(strSQL)
If r.EOF = False Then
intX = 0
vararray = r.GetRows()
For i = LBound(vararray) To UBound(vararray)
With frmCommitViewer.lstCases
.AddItem
.List(intX, 0) = r.Index(i)
End With
intX = intX + 1
Next i
End If
r.Close
c.Close
Set r = Nothing
Set c = Nothing
End Sub

I figured it out. Instead of the stupid array conversion, I'm still using the recordset directly.
If r.EOF = False Then
With frmCommitViewer.lstCases
.Clear
Do
.AddItem r![CASE_ID_NBR]
r.MoveNext
Loop Until r.EOF
End With
End If
Super simple. Thanks to http://www.fontstuff.com/vba/vbatut10pfv.htm.

Related

How to Transfer VBA UserForm Data To Access Database?

I have created a user form in excel to save my records in a sheets like sheet1.
But after few days working with this UserForm, it is now goes slower, because of heavy data saving in sheet1.
Now I want to save all records to a database and want to keep clean my sheet1.
So I can work on my UserForm easily or without any delay. Also wants updates my record by calling it via serial numbers.
but I don't want to keep any record in my sheet1.
my little code is below: -
Sub cmdAdd_Click()
On Error GoTo ErrOccured
BlnVal = 0
If BlnVal = 0 Then Exit Sub
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
Dim txtId, txtName, GenderValue, txtLocation, txtCNum, txtEAddr, txtRemarks
Dim iCnt As Integer
iCnt = fn_LastRow(Sheets("Data")) + 1
If frmData.obMale = True Then
GenderValue = "Male"
Else
GenderValue = "Female"
End If
With Sheets("Data")
.Cells(iCnt, 1) = iCnt - 1
.Cells(iCnt, 2) = frmData.txtName
.Cells(iCnt, 3) = GenderValue
.Cells(iCnt, 4) = frmData.txtLocation.Value
.Cells(iCnt, 5) = frmData.txtEAddr
.Cells(iCnt, 6) = frmData.txtCNum
.Cells(iCnt, 7) = frmData.txtRemarks
.Columns("A:G").Columns.AutoFit
.Range("A1:G1").Font.Bold = True
.Range("A1:G1").LineStyle = xlDash
End If
End With
Dim IdVal As Integer
IdVal = fn_LastRow(Sheets("Data"))
frmData.txtId = IdVal
ErrOccured:
'TurnOn screen updating
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
I will always be grateful to you.
Then, please try the next way. I will try creating of the necessary DB, table and fields using Excel VBA, too:
Copy the next piece of code which will create an empty DB, on the path you want:
Sub CreateEmptyDB()
Dim strPath As String, objAccess As Object
strPath = "C:\Your path\testDB"
Set objAccess = CreateObject("Access.Application")
Call objAccess.NewCurrentDatabase(strPath)
objAccess.Quit
End Sub
Programatically create the necessary table with its fields (`Start Date' added only to see how this type of data is handled...):
Sub createTableFields()
'It needs a reference to `Microsoft ActiveX Data Objects 2.x Library` (x = 2 to 9)
Dim Catalog As Object, cn As ADODB.Connection
Dim dbPath As String, scn As String, strTable As String
dbPath = "C:\Teste VBA Excel\testAccess\testDB.accdb"
strTable = "MySpecial_Table"
scn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & dbPath & ";"
Set Catalog = CreateObject("ADOX.Catalog")
Set cn = New ADODB.Connection
With cn
.Open scn
.Execute "CREATE TABLE " & strTable & " ([Name] text(255) WITH " & _
"Compression, " & "[Gender] text(255) WITH Compression, " & _
"[Location] text(255) WITH Compression, " & _
"[Address] text(255) WITH Compression, " & _
"[Number] number, " & _
"[Remarks] text(255) WITH Compression, " & _
"[Start Date] datetime)"
End With
cn.Close
End Sub
Add records to the newly created DB/Table:
Sub FillDataInDB()
'It needs a reference to `Microsoft ActiveX Data Objects 2.x Library` (x = 2 to 9)
Dim AccessDB As String, strTable As String, sql As String
Dim con As ADODB.Connection, rs As ADODB.Recordset, lastNo As Long
AccessDB = "C:\Teste VBA Excel\testAccess\testDB.accdb"
strTable = "MySpecial_Table"
Set con = CreateObject("ADODB.connection")
con.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & AccessDB
sql = "SELECT * FROM " & strTable
Set rs = CreateObject("ADODB.Recordset")
rs.CursorType = 1 'adOpenKeyset on early binding
rs.LockType = 3 'adLockOptimistic on early binding
rs.Open sql, con
If rs.RecordCount = 0 Then
lastNo = 0 'when no records in the table
Else
rs.MoveLast: lastNo = rs("Number") 'the last recorded value
End If
rs.AddNew
rs("Name") = "Test name" 'frmData.txtName
rs("Gender") = "Test gender" 'GenderValue
rs("Location") = "Test Location" 'frmData.txtLocation.Value
rs("Address") = "Test Address" 'frmData.txtEAddr
rs("Number") = IIf(lastNo = 0, 100, lastNo + 1) 'auto incrementing against the last value
'but starting from 100
'you can use frmData.txtCNum
rs("Remarks") = "Remarkable table..." 'frmData.txtRemarks
rs("Start Date") = Date
rs.Update
rs.Close: con.Close
Set rs = Nothing: Set con = Nothing
End Sub
Run the first two pieces of code in consecutive order (only once) and then start playing with the third one...
You can read the newly created DB Table (returning in an Excel sheet) in this way:
Sub ADO_Connection_ReadTable()
Dim conn As New Connection, rec As New Recordset, sh As Worksheet
Dim AccessDB As String, connString, query As String, strTable As String
AccessDB = "C:\Teste VBA Excel\testAccess\testDB.accdb"
strTable = "MySpecial_Table"
Set sh = ActiveSheet 'use here the sheet you want
connString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & AccessDB
conn.Open connString
query = "SELECT * from " & strTable & ";"
rec.Open query, conn
'return in the sheet
sh.cells.ClearContents
'getting data from the recordset if any and returning some in columns A:B:
If (rec.RecordCount <> 0) Then
Do While Not rec.EOF
With sh.Range("A" & sh.cells(Rows.count, 1).End(xlUp).row).Offset(1, 0)
.Value2 = rec.fields(0).Value
.Offset(0, 1).Value2 = rec.fields(3)
End With
rec.MoveNext
Loop
End If
rec.Close: conn.Close
End Sub
You can use a query to return specific data according to a specific table field. You can find plenty of examples on the internet.
I tried also showing how to handle an automate recording for the 'Number' field. Of course, if you are able to keep track of it in a different way, you can record it as you need/wont.
Please, test the above code(s) and send some feedback. You can use the DB path as a Private constant at the module level and much other ways to optimize the code. It is just a minimum workable solution only showing the way... :)

Access VBA run query with values passed from a list box

I have made this form in Access and I am hoping to do the following task.
The list box here contains two columns, and can be multi-selected. I want to use the values second column (the right column) and pass them into a query that I set up for the "test2" button below.
And here is my VBA code for the on-click event for the button.
Private Sub test2_Click()
Dim db As dao.Database
Dim qdef As dao.QueryDef
Dim strSQL As String
Set db = CurrentDb
'Build the IN string by looping through the listbox
For i = 0 To Select_Counties2.ListCount - 1
If Select_Counties2.Selected(i) Then
strIN = strIN & "'" & Select_Counties2.Column(1, i) & "',"
End If
Next i
'Create the WHERE string, and strip off the last comma of the IN string
strWhere = " WHERE County_GEOID in " & "(" & Left(strIN, Len(strIN) - 1) & ")"
strSQL = strSQL & strWhere
Set qdef = db.CreateQueryDef("User query results", strSQL)
qdef.Close
Set qdef = Nothing
Set db = Nothing
DoCmd.OpenQuery "User query results", acViewNormal
End Sub
I was getting this error:
Can someone tell me what I did wrong in the code? Thank you!
In this example from microsoft they call application.refreshwindow without explanation.
https://learn.microsoft.com/en-us/office/client-developer/access/desktop-database-reference/database-createquerydef-method-dao
What I think is going on is that your code fails because access cannot find the query that was just added to it's collection of queries. Also your generated sql is no longer valid.
So: replace my sql with your own valid sql
Private Sub test2_Click()
Dim db As DAO.Database
Dim qdef As DAO.QueryDef
Dim strSQL As String
strSQL = "PARAMETERS GEOID Number; " 'without valid sql this code doesn't run so
'replace my sql with your own.
strSQL = strSQL & "SELECT GEOID FROM Counties"
Set db = CurrentDb
For i = 0 To Select_Counties2.ListCount - 1
If Select_Counties2.Selected(i) Then
strIN = strIN & Select_Counties2.Column(1, i) & ","
End If
Next i
strWhere = " WHERE County_GEOID in " & "(" & Left(strIN, Len(strIN) - 1) & ")"
strSQL = strSQL & strWhere
Debug.Print strSQL
'now the important bit:
db.CreateQueryDef ("User query results") 'create the query
Application.RefreshDatabaseWindow 'refresh database window so access knows it has a new query.
'query will now be visible in database window. make sure to delete the query between runs
'Access will throw an error otherwise
Set qdef = db.QueryDefs("User query results")
qdef.SQL = strSQL
qdef.Close
Set qdef = Nothing
Set db = Nothing
DoCmd.OpenQuery "User query results", acViewNormal
End Sub

Type mismatch Error in VBA when creating ActiveX control drop down list

I got 3 ActiveX combo boxes in my sheet1.
I used some code in This Workbook to populate first combo box list. Then I have created some function to get next set of combo box for cascading values. below is function:
Function CascadeChild(TargetChild As OLEObject)
Dim Myconnection As Connection
Dim cmd As ADODB.Command
Dim Myrecordset As Recordset
Dim Myworkbook As String
Dim strSQL As String
Set Myconnection = New ADODB.Connection
Set cmd = New ADODB.Command
Set Myrecordset = New ADODB.Recordset
'Identify the workbook you are referencing
Myworkbook = Application.ThisWorkbook.FullName
'Open connection to the workbook
Myconnection.Open "--"
Select Case TargetChild.Name
Case Is = "Directorate"
strSQL = "Select Distinct Directorate AS [TgtField] from DBTable Where Division = '" & Sheet1.Division.Value & "' or 'All' = '" & Sheet1.Division.Value & "'"
Case Is = "Area"
strSQL = "Select Distinct Area AS [TgtField] from DBTable Where ( Division = '" & Sheet1.Division.Value & "' or 'All' = '" & Sheet1.Division.Value & "') AND (Directorate = '" & Sheet1.Directorate.Value & "' or 'All' = '" & Sheet1.Directorate.Value & "')"
End Select
'Load the Query into a Recordset
Myrecordset.Open strSQL, Myconnection, adOpenStatic
'Fill the target child listbox
With TargetChild.Object
.Clear
Do
.AddItem Myrecordset![TgtField]
Myrecordset.MoveNext
Loop Until Myrecordset.EOF
.Value = .List(0) '<<Automatically selects the first value in the ListBox
End With
'Clean up
Myconnection.Close
Set Myrecordset = Nothing
Set Myconnection = Nothing
End Function
Then I have written some code in Sheet1 in VBA:
Private Sub Division_Change()
Call CascadeChild(ActiveSheet.OLEObjects(Sheet1.Directorate.Name))
End Sub
Private Sub Directorate_Change()
Call CascadeChild(ActiveSheet.OLEObjects(Sheet1.Area.Name))
End Sub
First combo box giving values, then when I select value from ActiveX control, The error MSG populating with
Runtime Error, type mismatch
The Error coming with debug mode here .AddItem Myrecordset![TgtField]
Any help
Try
.AddItem Myrecordset.Fields(0).Value

UDF to paste recordset data in VBA

Basically, I have managed to retrieve the data from database to recordset by means of
rs=db.openrecordset(sql). How do I paste the data in the cell by UDF? Someone suggested array formula. Then how do i change recordset data to array? I know i can use copyfromrecordset . But it is not functioning in the UDF .
Thank you.
This is working for me with Excel 2003, ADO 2.8:
Function getArray(strSql As String) As Variant
Dim rs As ADODB.Recordset
Dim i As Integer
getArray = ""
Set rs = getRs(strSql)
With rs
.MoveFirst
Do
For i = 0 To .Fields.Count - 1
getArray = getArray & CStr(.Fields(i).Value) & " "
Next i
getArray = getArray & vbLf
.MoveNext
Loop Until .EOF = True
.Close
End With
Set rs = Nothing
End Function
It loops through all the rows/fields of a recordset and returns an "array" of values. It can be used as a workbook function without CSE.
This is how I'm making my db connection:
Function getRs(strSql As String) As ADODB.Recordset
Dim strCn As String
strCn = "Provider=sqloledb;Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security=SSPI;"
Set getRs = New ADODB.Recordset
getRs.Open strSql, strCn, adOpenStatic, adLockReadOnly
End Function
And this is a sample of how I could retrieve some data using getArray() based on criteria from one cell and return the results into another (single) cell.
Function getEmpDataByLastName(strLastName As String) As Variant
Dim strSql As String
strSql = ""
strSql = strSql & "SELECT BusinessEntityID, PersonType, FirstName, COALESCE(MiddleName,'') AS MiddleName "
strSql = strSql & "FROM Person.Person "
strSql = strSql & "WHERE LastName = '" & strLastName & "' "
strSql = strSql & "ORDER BY FirstName "
getEmpDataByLastName = getArray(strSql)
End Function

VBA to insert many records into access DB fast

OK so I have a spreadsheet that produces a reasonably large amount of records (~3500)
I have the following script that inserts them into my access db:
Sub putinDB()
Dim Cn As ADODB.Connection, Rs As ADODB.Recordset
Dim MyConn, sSQL As String
Dim Rw As Long, c As Long
Dim MyField, Result
Dim x As Integer
Dim accName As String, AccNum As String, sector As String, holding As String, holdingvalue As Double, holdingdate As Date
theend = lastRow("Holdings", 1) - 1
'Set source
MyConn = "S:\Docs\Harry\Engine Client\Engine3.accdb"
'Create query
Set r = Sheets("Holdings").Range("a2")
x = 0
Do
Application.StatusBar = "Inserting record " & x + 1 & " of " & theend
accName = r.Offset(x, 0)
AccNum = r.Offset(x, 4)
sector = r.Offset(x, 2)
holding = r.Offset(x, 1)
holdingvalue = r.Offset(x, 3)
holdingdate = r.Offset(x, 5)
sSQL = "INSERT INTO Holdings (AccName, AccNum, Sector, Holding, HoldingValue, HoldingDate)"
sSQL = sSQL & " VALUES ('" & Replace(accName, "'", "''") & "', '" & AccNum & "', '" & sector & "', '" & Replace(holding, "'", "''") & "', '" & holdingvalue & "', #" & holdingdate & "#)"
Debug.Print (sSQL)
'Create RecordSet
Set Cn = New ADODB.Connection
With Cn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.CursorLocation = adUseClient
.Open MyConn
Set Rs = .Execute(sSQL)
End With
x = x + 1
Loop While r.Offset(x, 0) <> "" Or x < 15
Application.StatusBar = False
End Sub
The trouble is, is that it loops through each record one-by-one, rebuilds and executes the query each time which results in very slow execution (about 2-3 records per second on my PC)
Is there a way to have vba insert the whole range into the DB in one go without having to loop through?
Thanks
The answer you have provided should improve things slightly as you only need open the connection once, but the code is still inefficient. You really only want to write to your recordset once with all the data rather than like this. I always prefer working from the Access side to pull info from Excel as oppose to pushing into Access from Excel but I believe we can use either for this scenario.
In this case your better to use DAO over ADO and work with a Transacation, essentially you still loop over the recordset but the actual act of writing the data does not happen until you Commit at the end so it's much faster.
This is a very basic example from the Access side for you to try:
Private Sub TestTrans()
Dim wksp As DAO.Workspace
Dim rs As DAO.Recordset
Set wksp = DBEngine.Workspaces(0) 'The current database
wksp.BeginTrans 'Start the transaction buffer
Set rs = CurrentDb.OpenRecordset("Table1", dbOpenDynaset)
Do 'Begin your loop here
With rs
.AddNew
!Field = "Sample Data"
.Update
End With
Loop 'End it here
wksp.CommitTrans 'Commit the transaction to dataset
End Sub
OK, silly me. After a bit of tinkering it turns out that putting the
Set Cn = New ADODB.Connection
With Cn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.CursorLocation = adUseClient
.Open MyConn
End With
bit outside the loop makes it far quicker.