First time asking here, so sorry if you find it annoying.
I've been trying to fill a ComboBox in an Excel Sheet with the result set of a DB procedure. So far, I've received the result set correctly, but when I try to specify the column I'd like to use to set the combo box values (3rd of the response), it keeps using the id's (1st column), even when I'm using the .BoundColumn command as follows:
cmd.ActiveConnection = conn
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "mySP"
Set rst = cmd.Execute()
With rst
i = .Fields.Count
vData = .GetRows
End With
With Sheet1
With .ComboBoxP
.Clear
.BoundColumn = 3
.List = Application.Transpose(vData)
.ListIndex = -1
End With
.ComboBoxP.Text = "-- Pick one --"
End With
Is there something wrong with the command order that makes the code to use always the same column? May it be an Excel version issue (I'm using MS Excel 2010 in spanish)?
You should be using the TextColumn property for setting which value gets shown to the user. The BoundColumn property sets which value your code gets when it reads the Value property of the control after the user has made a selection.
Related
I have a SQL query pulling back two columns and populating an ActiveX Combobox. The query in SQL returns a single row of data, however when I select the combobox in Excel the list is empty.
This is the part of the code that populates the combobox:
With rst
Set .ActiveConnection = Nothing 'Disconnect the recordset.
k = .Fields.Count
'Populate the array with the whole recordset.
vaData = .GetRows
End With
CB_Layer.List = Application.Transpose(vaData)
To test this I have added a watch to the rst and can see that I have one row of values. As soon as I pass over the vaData = .GetRows the watch value changes to:
: Value : <Either BOF or EOF is True, or the current record has been deleted. Requested operation requires a current record.>
Can anyone advise why this is happening?
Thanks in advance
I have to create a text box in MS Access where users are able to see the top 3 records of a particular result set. So even if the query results in 5 records I only want it to display the top 3 records as three textboxes (sometimes the result may also be 1,2 or 0 records).
I took the easy way out and created a new subform which was connected to the parent form using master/child field. The textbox was placed in the details part of the subform and as a recordsource of the subfrom used the following query:
Select top 3 tbl1.column1, tbl1.column2
from tbl1
column1 is the control source for the textbox and column2 is the column I have used for master/child link.
Now the catch is that the query works fine when I use it without top 3. But when I use top 3 the textbox suddenly disappears and the subform is completely blank.
I am not able to identify the cause of the error. My guess is that it has something to do with type of the subform. Not sure.
Is there any other way I can have a text box whose number can vary on the basis of the results?(but limiting the resultset to 3)
Appreciate the help.
Textbox are not meant to hold more than 1 value.
You are trying to assign three results of 2 columns to one textbox(No can do).
Use listbox to populate as you are doing, assigning the query you just wrote in the rowsource of the list(no subforms needed). This way users will see the three records.
You could use a textbox in order to accomplish what you are trying to do. But will require some VBA coding to accomplish this.
Public function CombineValuesForTextBox() as string
Dim rst as dao.recordset
Dim strSQL as string
strSQL = "SELECT TOP 3 tbl1.Column1 as field1, tbl1.Column2 as field2 " & _
"FROM tbl1;"
set rst = currentdb.openrecordset(strsql)
if rst.recordcount = 0 then 'Checks if the recordset has records or not
CombineValuesForTextBox = "No records found"
goto EndCode 'Or replace with what actions to take if no records are found
else
rst.movelast 'Forces the recordset to fully load
rst.movefirst
do while not rst.eof
if CombineValuesForTextBox = "" or CombineValuesForTextBox = empty then
CombineValuesForTextBox = rst![field1] & " - " & rst![Field2]
else
CombineValuesForTextBox = CombineValuesForTextBox & vbcrlf & _
rst![field1] & " - " & rst![Field2]
end if
Loop
end if
rst.close
set rst = nothing
EndCode:
if not rst is nothing then
rst.close
set rst = nothing
end if
end function
Then on your form put in the code (be sure the textbox is unbound...)
me.textbox = CombineValuesForTextBox
Lets say I got a table with an id, pre- and lastname. I made them work as comboboxes in the table. Now if I change 1 field (lets say the id) with the combobox I want that all the other stuff is changed too (the pre and lastname). How to achieve that (with a macro or vba, or is it easier)?
I solved it. I made a sub formular. Then i managed to dropdown the fields I wanted to. Copying the same table for finding the names ids...
And then i created an event for after update:
Private Sub PartnerIdServiceWorker_AfterUpdate()
Dim dbs As DAO.Database
Dim rst As DAO.Recordset
Set dbs = CurrentDb
Set rst = dbs.OpenRecordset("SELECT * FROM HTB WHERE PartnerIdServiceWorker = " & PartnerIdServiceWorker)
Do While Not rst.EOF
Me.AnredeMitarbeiter = rst!AnredeMitarbeiter
Me.VornameMitarbeiter = rst!VornameMitarbeiter
Me.NachnameMitarbeiter = rst!NachnameMitarbeiter
Exit Do
rst.MoveNext
Loop
rst.Close
Set rst = Nothing
Now I'm able to change the fields in the table and the other values do also change.
I have an MS Access (.accdb) table with data like the following:
Location Number
-------- ------
ABC 1
DEF 1
DEF 2
GHI 1
ABC 2
ABC 3
Every time I append data to the table I would like the number to be unique to the location.
I am accessing this table through MS Excel VBA - I would like to create a new record (I specify the location in the code) and have a unique sequential number created.
Is there a way to setup the table so this happens autmatically when a record is added?
Should I write a query of some description and to determine the next number per location, and then specify both the Location & Number when I create the record?
I am writing to the table as below:
Set rst = New ADODB.Recordset
rst.CursorLocation = adUseServer
rst.Open Source:="Articles", _
ActiveConnection:=cnn, _
CursorType:=adOpenDynamic, _
LockType:=adLockOptimistic, _
Options:=adCmdTable
rst.AddNew
rst("Location") = fLabel.Location 'fLabel is an object contained within a collection called manifest
rst("Number") = 'Determine Unique number per location
rst.Update
Any help would be appreciated.
Edit - Added the VBA code I am struggling with as question was put on-hold
I suspect that you are looking for something like this:
Dim con As ADODB.Connection, cmd As ADODB.Command, rst As ADODB.Recordset
Dim newNum As Variant
Const fLabel_Location = "O'Hare" ' test data
Set con = New ADODB.Connection
con.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\Public\Database1.accdb;"
Set cmd = New ADODB.Command
cmd.ActiveConnection = con
cmd.CommandText = "SELECT MAX(Number) AS maxNum FROM Articles WHERE Location = ?"
cmd.CreateParameter "?", adVarWChar, adParamInput, 255
cmd.Parameters(0).Value = fLabel_Location
Set rst = cmd.Execute
newNum = IIf(IsNull(rst("maxNum").Value), 0, rst("maxNum").Value) + 1
rst.Close
rst.Open "Articles", con, adOpenDynamic, adLockOptimistic, adCmdTable
rst.AddNew
rst("Location").Value = fLabel_Location
rst("Number").Value = newNum
rst.Update
rst.Close
Set rst = Nothing
Set cmd = Nothing
con.Close
Set con = Nothing
Note, however, that this code is not multiuser-safe. If there is the possibility of more than one user running this code at the same time then you could wind up with duplicate [Number] values.
(To make the code multiuser-safe you would need to create a unique index on ([Location], [Number]) and add some error trapping in case the rst.Update fails.)
Edit
For Access 2010 and later consider using an event-driven Data Macro and shown in my other answer to this question.
You need to add a new column to your table of data type AutoNumber.
office.microsoft.com: Fields that generate numbers automatically in Access
You should probably also set this column as your primary key.
For Access 2010 and newer, this is a better way to do it. It uses the table's Before Change Data Macro to derive the next sequential number and put it in the [Number] field of the new record:
The advantages of this approach are:
The sequence number will be applied whenever a new record is added, regardless of how it is added.
The Excel VBA code does not have to worry about creating the sequence number; it "just happens".
Since this code resides at the table level it should be safe for a multi-user environment.
For more information on Data Macros, see
Create a data macro
I have the following code in my Excel VBA that copies data from a table in SQL into Excel. This data is being inserted horizontally starting on cell C2, but I want it to be inserted vertically on column C.
Sheets("Control").Range("C2").CopyFromRecorset rsPubs
Where rsPubs is my ADO connection.
Basically, I just want this data transposed. What's an efficient way of doing this?
This is how rsPubs is created (the connection works fine as I'm actually getting the data):
' Create a recordset object.
Dim rsPubs As ADODB.Recordset
Set rsPubs = New ADODB.Recordset
With rsPubs
' Assign the Connection object.
.ActiveConnection = cnPubs
' Extract the required records.
.Open "SELECT * FROM Analytics.dbo.XBodoffFinalAllocation"
' Copy the records into cell B3 on Sheet1.
Sheets("Control").Range("C2").CopyFromRecordset rsPubs
' Tidy up
.Close
End With
cnPubs.Close
Set rsPubs = Nothing
Set cnPubs = Nothing
I cannot test this currently, but you could:
Sheets("Control").Range("C2").CopyFromRecorset rsPubs 'copy your data
Sheets("Control").Range("C2").Copy 'copy the data into clipboard
Sheets("Control").Range("C2").PasteSpecial xlPasteValues, xlPasteSpecialOperationNone, True, True
Also you could use the Transpose Worksheetfunction - however, I don't quite see a way right now to do this directly, expect your input data is transposed already.
Here is a nice official example and further informations on this topic: How to transfer data from an ADO Recordset to Excel with automation
Especially the "using GetRows" section.
This should do:
Dim resultset As Variant
Dim result As Variant
resultset = rsPubs.GetRows
result = Application.WorksheetFunction.Transpose(resultset)
Sheets("Control").Range("C2").Resize(UBound(result, 1), UBound(result, 2)) = result
http://www.teachexcel.com/excel-help/excel-how-to.php?i=147811
Edit 2 in the accepted answer doesn't work for me, but the following does (see http://www.mrexcel.com/forum/excel-questions/513845-copyfromrecordset-transpose.html for my source):
Public Sub PlaceTransposedResults(oResults As ADODB.Recordset, rTarget As Range)
Dim vTransposed As Variant
If Not oResults.EOF Then
vTransposed = oResults.GetRows
rTarget.Resize(UBound(vTransposed, 1) + 1, UBound(vTransposed, 2) + 1) = vTransposed
End If
End Sub
(this assummes that you haven't changed the array base with the OPTION BASE and that your version of Excel has Range.Resize and that oResults is never nothing)
One tweak on this is to make this a function and return the correctly sized range - useful if you want to resize a named range to cover the result set.
Another likely tweak is that you may want to optionally allow the user to ask for the field names to be added as the in the first column. I have found nothing better than:
Dim ix As Integer
For ix = 0 To oResults.Fields.Count - 1
rTarget.Offset(ix, 0) = oResults.Fields(ix).Name
Next ix
(of course you then have to offset your main results by 1 column in this case).
Untested:
Sub CopyTransposed(rng As Range, rs As ADODB.Recordset)
Dim x As Long, y As Long
x = 0
Do While Not rs.EOF
For y = 0 To rs.Fields.Count - 1
rng.Offset(y, x).Value = rs.Fields(y).Value
Next y
x = x + 1
rs.MoveNext
Loop
End Sub
This is really simple. It uses Excel's built-in transpose. Can be transposed in a single statement. No looping is needed. Tested and validated.
Dim vRecs, vRecsX
vRecs = rsPubs.GetRows
vRecsX = Application.Transpose(vRecs)
Sheets("Control").Range("C2").Resize(rsPubs.RecordCount, rsPubs.Fields.Count) = vRecsX
This code is copied directly from a working example.
Note, since this is a worksheet function, expect it to work with arrays of size only up to the number of columns on a worksheet. I believe columns-per-sheet varies between different versions of Excel, so it's a safe bet to determine your transpose limit based on your version of Excel.
"In the modern versions of Excel this is column XFD, which is 16,384 columns. In older versions of Excel (2003 and prior) the last column was IV which is 256 columns."
https://officemastery.com/_how-many-rows-and-columns-in-excel/
My suggestion is not to use VBA, at all. Microsoft already provided you the facility to import data from a database.
Data -> Import External Data
It will, then, create a QueryTable inside the sheet where you can just right-click and refresh in a regular basis. Another bonus is that you don't get the nasty macro warning. The QueryTable can be a table, query or stored procedure.
Try it!