I am trying to retrieve records from Oracle SQL database and write them into a dictionary. Catch is that this code works as expected on my machine but no on my coworker's. On his end Recordset retrieves only one record even if that Recordset has RecordCount higher than one, meanwhile that same query pull up multiple records for me.
Initially I thought that MoveNext method is causing problems but once I switched to GetRows method issue persists. Anyway since this code works on my machine I'm fairly certain that my code correct, but you never know so I am including function in question here. The commented part contains the loop when I was using MoveNext but like I said changing that loop does not help with my problem.
Public Function WriteQueryToDict(SQL As String) As Dictionary
'First column of the query must contain key that will be used to locate records in the dictionary
'Output is a dictionary with first column as key, and the rest in an array that is indexed from 0
Dim Results As Recordset
Dim ResultsArray() As Variant
Dim cursorField As Integer
Dim cursorRow As Integer
Dim Output As Dictionary
Dim key As Variant
Dim record() As Variant
Set Results = RunQuery(SQL)
Set Output = New Dictionary
If Not (Results.BOF And Results.EOF) Then
' New loop
ResultsArray = Results.GetRows()
ReDim record(0 To (UBound(ResultsArray, 1) - 1))
For cursorRow = 0 To UBound(ResultsArray, 2)
key = ResultsArray(0, cursorRow)
For cursorField = 1 To UBound(ResultsArray, 1)
record(cursorField - 1) = ResultsArray(cursorField, cursorRow)
Next
Output.Add key, record
Next
' Original loop
'Results.MoveFirst
'Do
' For cursorField = 1 To Results.fields.Count - 1
' record(cursorField) = Results.fields(cursorField).Value
' Next
'
' key = Results.fields(0)
' Output.Add key, record
' Results.MoveNext
'Loop Until Results.EOF
End If
Set WriteQueryToDict = Output
End Function
In case this might be relevant this is function that does the actual connection + querying:
Private Function RunQuery(SQL As String) As Recordset
Dim Username As String
Dim Password As String
Dim DBConn As ADODB.Connection
Username = ThisWorkbook.Sheets("Configuration").Range("C3").Value
Password = ThisWorkbook.Sheets("Configuration").Range("C4").Value
Set DBConn = New ADODB.Connection
With DBConn
.Provider = "OraOLEDB.Oracle.1;user id = " & Username & "; password = " & Password
.CONNECTIONSTRING = "Data Source=" & DB
.Open
End With
Set RunQuery = DBConn.Execute(SQL)
End Function
I am guessing that this has to do with some setting set outside of my code, my colleague has no knowledge to configure oracle drivers on his own so I am at loss as to what tis might be. Anyway I've never seen my code produce such differences between two different machines so I'm not sure what am I looking for to fix this problem, I will share additional details as needed if you can tell what might be missing here.
Thank you
UPDATE:
When debugigng I setup a breakpoint inside WriteQueryToDict right before the loop starts, that way I was able to Play with Recordset itself first check the RecordCount property to make sure that we have records to iterate over last time I ran this on his machine I saw about 60 records, then call MoveFirst and my epectation is that now I will be able to call MoveNext 60 times before EOF turns true, instead it turn true after first call. while at it I tried setting cursorLocation to Client but that didn't have any effect.
Related
I am looking to return a a column of data to be used as a combo box within excel. To do this I am using a recordset to return my data from a stored procedure. The problem I am having is that I can't figure out how to populate or even display the information I am returning - or even if I am returning all the data.
I know there are solutions online that recommend using "rs.GetRows" to populate an array, however I have had no luck with this. I had originally intended to populate an array by indexing through the records in the recordset and adding them individually. The problem with this is I can never get the size of the recordset and because I am using dynamic SQL the size of my array is never the same. I have been at this for a while and I was wondering if there is a good way to do this.
VBA Code
Public Sub SQL_SP_Column_Data2(ByVal sql_col As String, _
ByVal sql_table As String)
On Error GoTo RunTimeError
Dim array1() As Variant
Dim sqlconxn As ADODB.connection
Dim sqlcmd As ADODB.Command ' Operates as a command between SQL and vba
Dim sqlrs As ADODB.recordSet
Dim conxnString As String
Application.ScreenUpdating = False
' String used to establish a connection to the database
conxnString = "driver={SQL Server};" & _
"server='server_name';" & _
"uid='username';" & _
"pwd='password';" & _
"database='database_name';"
Set sqlconxn = New ADODB.connection
Set sqlcmd = New ADODB.Command
Set sqlrs = New ADODB.recordSet
sqlconxn.ConnectionTimeout = 30
sqlconxn.Open conxnString ' makes the connection between SQL
MsgBox "Connection 1 state: " & GetState2(sqlconxn.state)
sqlcmd.CommandType = adCmdStoredProc ' sets command to a stored procedure
sqlcmd.CommandText = "GET_COLUMN_DATA" ' name of the stored procedure
sqlcmd.ActiveConnection = sqlconxn ' makes the sql connection
sqlcmd.Parameters.Append _
sqlcmd.CreateParameter("#column_name", adVarChar, adParamInput, 255, sql_col)
sqlcmd.Parameters.Append _
sqlcmd.CreateParameter("#data_table_name", adVarChar, adParamInput, 255, sql_table)
sqlrs.Open sqlcmd
If sqlrs.EOF Then
array1() = sqlrs.GetRows
Debug.Print "VALUES: " + array1(i)
sqlrs.MoveNext
i = i + 1
End If
RunTimeError: ' Reportd any errors that might occur in the system and
Dim strError As String
strError = "ERROR: " & Err.Number & vbCrLf & Err.Description
MsgBox strError
Debug.Print strError
Exit Sub
End Sub
I am using GetRows above because it was the most recommended method, but I can't get it to work the way I am intending it to. In this example I usually get ERROR: 0 or subscript out of range.
SQL Stored Procedure
USE AFCD
GO
ALTER PROCEDURE dbo.GET_COLUMN_DATA (#column_name nvarchar(max),
#data_table_name nvarchar(max)) AS
BEGIN
DECLARE #query nvarchar(max)
SET #query = 'SELECT ' + #column_name +
' FROM ' + #data_table_name + ''
EXEC sp_executesql #query
END
When I test this stored procedure I actually get the data that I want to I know my stored procedure is returning what I want, but I don't know how to get that within vba.
Example of expected outputs:
If the inputs into the the vba sub are - "WIRE_TYPE", "WIRE_INDEX"
SQL_SP_Column_Data2 "WIRE_TYPE", "WIRE_INDEX"
Then the output should look like this:
Stainless Steel
Steel ER70S-3
Steel ER70S-6
Titanium(1)
Titanium(2)
SOLUTION - This part of code refers to my vba and is right after I execute my SQL SP. Credit to Tim Williams for the answer.
If Not sqlrs.EOF Then
array1() = sqlrs.GetRows()
End If
Dim i As Integer
For i = 0 To 4
Debug.Print "VALUES: " + array1(0, i) ' 2D array!!!
Next i
Your If test is off, and GetRows doesn't need a loop:
If Not sqlrs.EOF Then
array1() = sqlrs.GetRows()
End If
Remember GetRows() returns a zero-based 2-D array, so you need to provide both dimensions when accessing values eg.
array1(0, 2) 'first field, third row
I have a function that obtains a recordset of attributes from Active Directory.
I can access most of the returned values like this
Set rs = cmd.Execute
Debug.Print (rs.Fields("distinguishedName").Value)
Debug.Print (rs.Fields("GivenName").Value)
However the returned value for postOfficeBox looks like it could be a variant.
How do I access the value? I tried
Debug.Print (rs.Fields("postOfficeBox").value(0))
That did not work - Run Time Error 450.
Thanks Tim Williams. I manage to get it to return a value like this:
Dim arrPOBox As Variant
Dim strPOBox As String
....
Set rs = cmd.Execute
arrPOBox = rs.Fields("postOfficeBox").Value
strPOBox = CStr(arrPOBox(0))
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.
Hi guys i am new here and i am new to vba.
i want to solve the following problem:
i have two different access tables. each of them contains data i want to compare first and then, if a certain constraint is true i want to import certain columns out of one of the two access db tables into an excel sheet.
what i already have: the connection to the databases, i can read the data and print them on the console via debug.print command.
i have really no idea how to write certain rows (those which conform to the constraint) to the excel sheet.
Code sample
'commandstring and data base variables stands here
'non database connection variables
Dim oldID, newID, oldBuildPlanned, newBuildPlanned As String
Dim createExcel, doesExcelExist As Boolean
Dim xl As Excel.Application
Dim wb As Excel.Workbook
Dim Wksht As Excel.Worksheet
Dim dataVar As String
Dim counter As Integer
counter = 0
createExcelSheet = False
doesSheetExist = False
'Debug.Print "TEST old database"
Do While Not objRs.EOF And Not objRs2.EOF
'Debug.Print vbTab & objRs(0) & " " & objRs(1)
'assigning database values to variables to make them comparable
oldID = objRs(counter)
newID = CStr(objRs2(counter))
oldBuildPlanned = objRs(counter + 1)
newBuildPlanned = objRs2(counter + 1)
If oldID = newID And oldBuildPlanned = newBuildPlanned Then
createExcel = True
If createExcelSheet = True And Not doesSheetExist = True Then
Set xl = New Excel.Application
Set wb = xl.Workbooks.Add
Set Wksht = ActiveWorkbook.Worksheets("Sheet1")
doesExcelExist = True
End If
Call writeReport(newID)
End If
objRs.MoveNext
objRs2.MoveNext
Loop
'tidy up stuff comes here
end of code
I am sorry if my code is not formatted as its usual, its my first post in this forum ^^
So the writeReport() should contain the code to write the data into the sheet. i planned to insert the id's of the matching database entries into the method as parameters and read these certain data out of the recordset. but i cannot convert recordset items to string, so the byRef parameter declaration causes a compile error "type mismatch". In addition i tried to export the table with DoCmd.TransferSpreadsheet, but this method exports the entire table into excel, it worked, but it is not what i am searching for.
i hope someone can help me with my little low level problem, if you need further information feel free to ask me. i am using ADO.
Thanks in advance
Welcome to the forum,
I think you might find these two websites helpful for what you are trying to do. The first one is a great tutorial on using Access and Excel together.
http://www.datawright.com.au/excel_resources/excel_access_and_ado.htm
http://www.w3schools.com/sql/default.asp
I am not sure how you are creating your recordset, by I would recommend using an SQL statement as your source. That way you only pull the data from Access that you need. If you have any more specific questions, please let me know.
Jason
I have to write some VBA code in excel to go with my VB.NET program and I am struggling with the basics of VBA as I have not used it before. I found some code which allowed me to get the values from my temporary table and put them directly in appropiate fields in the excel spreadsheet. I am populating a purchase order document, so information passed over are things like subtotal, vat, shipping, total, etc.
This is the code I have used to populate a single cell:
'get quantity
strQry = "SELECT quantity from [temp];"
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = cn
.Open strQry
End With
'append data to document
Worksheets("PurchaseOrder").Range("D22").CopyFromRecordset rs
quantity = rs.Fields("quantity") 'setting the quatity in a variable
The last line of code is my attempt of storing the value of quantity in a variable, which I need to use to calculate the sub total as sub total was not passed over to excel. The code to populate the cells works fine, it is just putting the data in a variable to manipulate which I am struggling with. Populating the cells directly from the database works fine, but I am getting an error on that last line.
After storing quantity in a variable, I would also like to store the cost per unit in a variable, remove the £ sign at the start using a substring equivalent, convert it to a decimal then times the cost per unit by the quantity to get the sub total.
I tried using this following code:
'get price
strQry = "SELECT costPerUnit from [temp];"
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = cn
.Open strQry
End With
'append data to document
Worksheets("PurchaseOrder").Range("N22").CopyFromRecordset rs
costPerUnit = Right(rs(0), Len(costPerUnit) - 1) 'setting the cost per unit in a variable
subtotal = costPerUnit * quantity
Worksheets("PurchaseOrder").Cells("Q47").Value = "£ " & subtotal
Any help is appreciated. Thankyou.
Ok. So until filling the of the Recordset you have done it correct and then you have to see that the Recordset is more like a table and can have none or multiple rows with multiple columns.
First you have to check if the query returned any result or if the Recordset is empty (rs.BOF and rs.EOF are true). Then you would loop through the rows (rs.MoveNext).
To access a single value you can either give the index of the column or the column name.
The following example loops through rows and through columns but also extracts again the column "quantity" at the end:
If (rs.EOF) And (rs.BOF) Then
Exit Function
Else
rs.MoveFirst
Do Until rs.EOF
For j = 1 To rs.Fields.Count
valueOfColumnJ = rs.Fields(j - 1).Value
Next j
quantity = rs.Fields("quantity")
rs.MoveNext
Loop
End If
Recordsets have a cursor and any reference to the recordset is going to return properties (like the value of a field) based on where that cursor is.
When you called CopyFromRecorset, you moved the cursor to the end (EOF = True). Then when you tried to get the Fields("quantity"), there was no active record of the recordset, so you got an error.
You could have first done rs.MoveFirst if you have the right type of recordset. Then quantity would have equaled the quantity field from the first record. Probably not what you want.
There is not a one-liner (as far as I know) that will get you the total of all the fields in your recordset. You have to loop like Graffl shows.
What might be the better path is to use Excel. You already have the data in there, so insert a formula to get the subtotal you want.
Worksheets("PurchaseOrder").Cells("Q47").Formula = _
"=SUMPRODUCT(P23:P46*Q23:A46)"
or something like that.
you can save Recordset to array
as shown below
Sub Check_gg_date_time_Unique(i As Integer)
Dim filmSet As New ADODB.Recordset
Dim dbConn As New ADODB.Connection
Dim filmName As String, lastrow As Integer
Dim Varray As Variant
dbConn.ConnectionString = "Provider=MSDASQL.1;Data Source=Excel_to_MySQL_Maintenance"
dbConn.Open
Set filmSet = dbConn.Execute("select count(wono) from maintennce_db.work_order where post_gg_date_time = '2022-08-30 14:21:37'")
Varray = filmSet.GetRows(1)
If Varray(0, 0) = 0 Then
'Do whatever you want
End If
End Sub