I have a table that contains data that I am trying to import into a spreadsheet control on a userform in vba/excel.
The results will be viewed by an end user, so I have set the value of the header cells on initialization as opposed the the column headings from the sql table.
My query looks something like this
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sConnString As String
sConnString = "Provider=SQLOLEDB;Data Source=mysource;Initial Catalog=mydatabase;User Id=myusername;Password=mypassword;QuotedID=No"
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
conn.Open sConnString
Set rs = conn.Execute("SELECT * FROM WOFT_tbl_clients WHERE userid = '" & UserId.Value & "';")
If Not rs.EOF Then
/////////////////
/something here!/
/////////////////
Else
MsgBox "Error: No records returned.", vbCritical
End If
If CBool(conn.State And adStateOpen) Then conn.Close
Set conn = Nothing
Set rs = Nothing
What I am trying to do is get the output of selected columns from the database and be able to feed them into whatever colomn I like on the spreadsheet control.
So in effect I would somehow like a loop that allows me to output the resultset of columns id, name and userid into the spreadsheet control starting from row 2. My database also contains many other columns which are not needed in this spreadsheet, but will be needed for another spreadsheet control on the same userform, some of which will appear on both.
What I would like to be able to do is have each column in its own recordset, so I could have something like ids stored in a id recordset which I could then use in column A in spreadsheet control 1, and colomn 6 in spreadsheet control 2?
I hope this makes sense! I am using Excel 2010
To have each data column from one table in an extra recordset would lead you to isolated data lists, which wouldn't follow any logic of a relational database model. If the data indeed is isolated in the way, that id is a list, name is another list and so on, living in the same table, then - forgive me - your database model is quite bad.
If you nevertheless want to get only certain columns from your SQL database, specify them in the SQL statement:
SELECT id, name, userid FROM tablename WHERE anycondition ORDER BY name
To loop through every record the recordset object returns, use a do-until loop like this. Don't forget the MoveNext, or it may result in an endless loop.
Do Until rs.EOF
... do anything you want with the record ...
rs.MoveNext
Loop
I hope this is what you needed. Feel free to further explain, if I didn't understand your needs correctly.
Related
I have a requirement in MS Access where a table is displayed as several rows in the form. I have created one form detail record(several fields) that will repeat for each row in the Table1. Lets say I have five columns in the Table1. Based on Column3 value, I would like to have a list of value for Column4 and Column5 during form_load. I have also created a separate Table2 to establish relationship between Column3, Column4 and Column5. I have set up Event procedure to populate the values using sub function. The challenge I have is, not being able to set up different listbox 'value list' for different rows. Any tips on populationg form fields IMRecomExIns and AmendReasonExIns by processing each row in Table1 would be a great help.
Private Sub IMRecomExIns_Click()
Dim CoverType As String
Dim ListRecomm As String
Dim ListAmend As String
Dim db As DAO.Database
Dim tablevar As Recordset
Set db = CurrentDb
Set tablevar = db.OpenRecordset("Table2")
CoverType = "*" & Me.CoverTypeExIns.Value & "*"
ListRecomm = ""
ListAmend = ""
If tablevar.EOF = False And tablevar.BOF = False Then
tablevar.MoveFirst
Do Until tablevar.EOF
If tablevar!CoverType Like CoverType Then
ListRecomm = tablevar!Recommendation
ListAmend = tablevar!AmendReason
tablevar.MoveLast
End If
tablevar.MoveNext
Loop
End If
Me.IMRecomExIns.RowSourceType = "Value list"
Me.IMRecomExIns.RowSource = ListRecomm
Me.AmendReasonExIns.RowSourceType = "Value list"
Me.AmendReasonExIns.RowSource = ListAmend
End Sub
1) I have stored all the value list in a single cell. For example tablevar!Recommendation will have all the values for Me.IMRecomExIns.RowSource, which means the output is will look like "Rec1";"Rec2";"Rec3";etc... Same applies for tablevar!AmendReason "AR1";"AR2';"AR3";ETC... Understand this is not the normalized form of storing data. I want to POC to work before building a full solution with normalized tables.
2) Answered earlier.. the rowsource will be set with all the possible values at the first match, so no point in going all the way to the end of the table
3) CoverTypeExIns is a string, Table 2 have many different possibilities such as "Mortgage Income" and "Family Income", however the Recommendation and Amendreason are same for all "Income" category that comes from Table1. Thats why the wildcard search.
My problem is not with setting the RowSource for a single row, but setting up RowSource for multiple occurrence in of the same IMRecommmendation and AmendReason in MS Access screen.
Here is the design view of the form. This form is linked to MS Access table. For multiple rows the Detail record will repeat itself as many times.
An example of two rows displayed in the screen.
I'm not sure exactly what you are asking/trying to do here.
I can see at several problems with the code that you have:
You are using tablevar.MoveLast in the loop, whic would automatically take you to the end of the recordset.
Also, you are not concatenating (joining together) ListRecomm/ListAmend, you are just setting them equal to a value, so each loop that matches will overwrite any previous value.
Finally, I am not sure what you are doing with trying to find CoverTypeExIns - you are using LIKE, which would indicate that it is text, but not wrapping the value in single quotes. If it is a numeric value, then you should be using "=".
However, rather than opening a recordset, looping it and checking for a match to build up a list of values, it is better to just set the RowSource of listboxes equal to a SQL string (effectively a query).
Something like (assuming CoverType is numeric):
Private Sub IMRecomExIns_Click()
Dim strSQL As String
strSQL = "SELECT Recommendation FROM Table2 WHERE CoverType=" & Me!CoverTypeExIns
Me!AmendReasonExIns.RowSource = strSQL
End Sub
I prefer to declare a string to hold the SQL statement rather than setting the .RowSource directly, as it makes troubleshooting easier.
Regards,
Based on the new information given, below is some VBA code that opens up a recordset based on the information entered in "Cover", and then sets the .RowSource property of the two combo boxes to be the value lists. In my example, don't bother setting the .RowSourceType, as this should be done at design time:
Private Sub IMRecomExIns_Click()
Dim db As DAO.Database
Dim rsData As DAO.Recordset
Dim strSQL As String
Set db = DBEngine(0)(0)
strSQL = "SELECT Recommendation, AmendReason FROM Table2 WHERE CoverType LIKE '*" & Me!cboCover & "*';"
Set rsData = db.OpenRecordset(strSQL)
If Not (rsData.BOF And rsData.EOF) Then
Me!IMRecomExIns.RowSource = rsData!Recommendation
Me!AmendReasonExIns.RowSource = rsData!AmendReason
End If
rsData.Close
Set rsData = Nothing
Set db = Nothing
End Sub
As I have previously stated, you should really normalize the design of your database now, rather than getting in so far that it requires a major re-write.
Regards,
I am trying to use unbound comboBoxes and textBoxes where a user a updates the controls and clicks on a button and a new record is created in another tblEntry using some data of the same record from tblItems.
Problem: My code only works on the first record. It creates the new record in the tblEntry using data of the first record in tblItems. Can someone have a look please?
Private Sub addItem_Click()
Dim rs1 As DAO.Recordset
Dim rs2 As DAO.Recordset
Set rs1 = CurrentDb.OpenRecordset("SELECT * FROM tblItems")
Set rs2 = CurrentDb.OpenRecordset("SELECT * FROM tblEntry")
If Not IsNull(Me.combo1) Then
rs2.AddNew
rs2.Fields("Description").Value = rs1.Fields("Description").Value
rs2.Fields("ItemNo").Value = rs1.Fields("ItemNo").Value
rs2.Fields("ItemName").Value = Me.txtItemName.Value
rs2.Fields("entryDate").Value = Me.txtentryDate.Value
rs2.Update
Form.frmItemEntryDatasheet.Requery
End If
rs1.Close
Set rs1 = Nothing
rs2.Close
Set rs2 = Nothing
End Sub
As #June7 says, there probably is no reason to do this. However, what you need to do is to open rs1 up filtered to just show data relating to that selected in combo1. Assuming that the first column in combo1 is the Primary Key from tblItem and called "ItemID":
Set rs1 = CurrentDb.OpenRecordset("SELECT ItemDescription, ItemNo FROM tblItems WHERE ItemID ='" & ItemID.Value & "'")
I have also renamed your field "Description" to "ItemDescription" as it is probably a reserved word within Access and may case problems. I have also just selected the 2 fields that you are going to use - there is no point getting all of the fields. You should be opening both recordsets within the If/End If statement.
Also, when you are opening rs2, you are effectively selecting the whole table. Far better is to use:
Set rs2 = CurrentDb.OpenRecordset("SELECT * FROM tblEntry WHERE 1 = 2")
This opens up a recordset based on tblEntry, but with no records selected, and so therefore has less overhead.
Regards,
What I am trying to do is to get some data from an online server through an SQL Query and then loop through the recordset modifying the records.
I get an error when trying to modify the recordset:
"Multiple-Step operation generated errors. Check each status value."
My question is: Is there a way of modifying a record from a recordset that I got from a Query?
In this case I am modifying field 2 if field 1 meets a certain criteria. (In this case Field 2 is a string)
Here is the simplified code:
Dim adoConn As ADODB.Connection
Dim locRS As New ADODB.Recordset, proRS As ADODB.Recordset
Dim strConnection As String
Set getSQL = New ADODB.Recordset
'Set Objects
Set adoConn = New ADODB.Connection
'Specify connection string
strConnection = "User ID=xxx; Password=xxx;Data Source=xxx;Provider=OraOLEDB.Oracle"
'Open the connection
adoConn.Open (strConnection)
'Set up recordset properties
getSQL.CursorType = adOpenStatic
getSQL.CursorLocation = adUseClient
getSQL.LockType = adLockBatchOptimistic
'Import the data
getSQL.Open "SELECT FIELD1, FIELD2 FROM TABLE", adoConn, adOpenStatic, adLockOptimistic
Set getSQL.ActiveConnection = Nothing
getSql.Update
'Loop through data
getSQL.MoveFirst
Do While Not stockRS.EOF
'If cetrain condition is met then modify the null column
if getSQL!FIELD1=CRITERIA then
'Error here
getSQL!FIELD2="SOME STRING"
End If
getSQL.MoveNext
Loop
'Close
adoConn.Close
Set adoConn = Nothing
Your SQL is not doing what you think:
SELECT ... NULL OUTCOME ... is going to return the value NULL in a field called OUTCOME but will not link to a field in the table called OUTCOME (which is what I think you are looking for) as your current syntax is setting up an ALIAS not selecting the field. I am assuming the field OUTCOME exists on the table. If not you need to create it up front or do an alter table to add the field before you can write anything to it.
I recommend creating field up front (which I think you have already done). But make sure that the default value is NULL so you don't need to do your NULL trick in the select ALSO make sure that the field is allowed to take a NULL value or you will see errors. Select becomes:
getSQL.Open "SELECT FIELD1, FIELD2, OUTCOME FROM TABLE", adoConn, adOpenStatic, adLockOptimistic
And then manage the NULL value in the function as follows:
if getSQL!FIELD1=CRITERIA then
'Error here
getSQL!OUTCOME="SOME STRING"
ELSE
getSQL!OUTCOME=NULL
End If
This ensure that you always write something to OUTCOME field so processing and OUTCOME don't get out of sync.
Also I still think that you have divorced the recordset data from the server when you:
Set getSQL.ActiveConnection = Nothing
Do this to release resources after your are done.
You may also need a
getSql.Update
After making changes to commit them back to database.
can this be done, say I have rows (1,2,3,4,5) and I want to grab three rows, select one randomly and then get it's neighbors, so maybe the random selection is row (3), I can also grab (2,4) if I wanted its neighbors, do I just pick one at random and then look for the unique key before and after like this or can I do it all in one sql statement.
I was going to use ADO from excel to pull records (so VBA connects to access, opens a recordset with sql instructions and so on).
Hope I was clear!
I would love to just do this all in a SQL statement
I am not sure Access is capable of all the SQL commands such as SQL Server, so this may be a bit of a problem. If you have a primary key though, you can easly generate a Select query in VBA and then pass open recordset with this SQL.
Dim sSQL as String
Dim lRand as Long
Dim rs as ADODB.Recordset 'or DAO.Recordset'
lRand = VBA.Int(VBA.Rnd() * TableRecordCount) ' TableRecordCount is the number of records in the table that you need to get somehow'
sSQL = "SELECT * FROM TableName WHERE (ID>=" & lRand - 1 & " AND ID <=" & lRand + 1
set rs = CurrentDB.OpenRecordset(sSQL, ...)
I am now not absolutely sure of what you want to use and depending on ADODB or DAO choice, you need to open the recordset accordingly with wither Call rs.Open or Set rs = DB.OpenRecordset
After I connect to SQL Management Studio via Excel VBA, I have the following code (PART OF IT) that pulls the data from a table in SQL.
With BalanceSheet
' Assign the Connection object.
.ActiveConnection = cnPubs
' Extract the required records.
.Open "select * from Analytics.dbo.BalSheetKeyLineItemsFinal"
Sheet1.Range("A13").CopyFromRecordset BalanceSheet
' Tidy up
.Close
End With
HOWEVER, the column names from the table do not show up. Is there a way I can get the column names to be included when my data is exported to Excel??
Thanks!
A recordset contains the data you have queried against, so this is what you would expect to happen. Since you're using .Open and .ActiveConnection, I assume you're using ADO: In which case, you can get the field names from the recordset object's Fields collection:
For i = 0 to .Fields.Count - 1
Sheet1.Range("A12").Offset(, i) = .Fields(i).Name
Next
This will write the field titles across row 12 of your Sheet1.