I am running an SQL query on an oracle DB, it is just a simple select * from table query. This works fine for a small table 900 rows and 5 columns. But when I try it on a table with say 30 columns and 95,000 rows I get the below error. I tried increasing MaxRecords but to no avail.
Error
Code
Public Const sConnect As String = "Driver={Microsoft ODBC for
Oracle};Server=server;Uid=user;Pwd=password"
Sub GetData1()
i = 0
Sheets(1).Range("a1:ao20000").ClearContents
Dim rsConnection As ADODB.Connection
Dim rsRecordset As ADODB.Recordset
Dim sQuery As String
sQuery = "select * from trade"
Set rsConnection = New ADODB.Connection
Set rsRecordset = New ADODB.Recordset
rsConnection.ConnectionString = sConnect
rsConnection.Open
rsRecordset.MaxRecords = 1048575
Set rsRecordset = rsConnection.Execute(sQuery)
Worksheets(1).Range("A2").CopyFromRecordset rsRecordset
For i = 0 To rsRecordset.Fields.Count - 1
Worksheets("Sheet1").Cells(1, i + 1).Value = rsRecordset.Fields(i).Name
Next i
rsConnection.Close
Set rsConnection = Nothing
Set rsRecordset = Nothing
End Sub
Regards,
Tim
After a little more digging I found the answer. It relates to database fields which are TIMESTAMP(6). I have to redo my db query to do a to_char(timestamp_field) and all is OK.
Thanks
Tim
Related
I am working on a project where I'm using SQL to return records from a database into a listbox using userinput. Things are fine when there's more than one result, the listbox reads properly. However, when there's only one record, it doesn't transpose it like it does with all the others. Multiple Returns Single Return
I've tried to do an "if employeeoutput.recordcount = 1 then don't transpose" type of thing, but that still sends me with the Single Return.
Private Sub cmdSearch_Click()
Stop
'connect to database
'make dynamic code
'profit
'set up DB and Recordset
Dim calldbconn As ADODB.Connection
Dim EmployeeOutput As ADODB.Recordset
Dim EmpData As Variant
'its a special tool we're going to use later
Dim InContactInput As String
Dim FNameInput As String
Dim LNameInput As String
Dim TeamNameInput As String
Dim TeamNumberInput As Integer
Dim SQLString As String
'set it up for a new recordset
Set calldbconn = New ADODB.Connection
Set EmployeeOutput = New ADODB.Recordset
'set the vars
InContactInput = txtInContactNum.Value
FNameInput = txtFirstName.Value
LNameInput = txtLastName.Value
TeamNameInput = cboTeamName.Value
TeamNumberInput = cboTeamName.ListIndex + 1
'---------A bunch of stuff here to build the SQL String----------
'----------------------------------------------------------------
'Time to connect
calldbconn.ConnectionString = StrCallDB
calldbconn.Open
On Error GoTo CloseConnection
With EmployeeOutput
.ActiveConnection = calldbconn
.Source = SQLString
.LockType = adLockOptimistic
.CursorType = adOpenKeyset
.Open
EmpData = .GetRows
On Error GoTo CloseRecordset
End With
'And now, the list box
With lstResults
.Clear
.List = Application.Transpose(EmpData)
End With
'close and reset
CloseRecordset:
EmployeeOutput.Close
CloseConnection:
calldbconn.Close
Set EmployeeOutput = Nothing
Set calldbconn = Nothing
End Sub
Please help me. Excel VBA is throwing this error: Multiple-step OLE DB Operation generated errors
on the line : cmd.Parameters("[days]") = ActiveWorkbook.Sheets("MABI_Extracts").Range("B18").Value
I'm trying to run a query in access named "AGBA_Conversion" with an integer parameter. How do I resolve? thanks in advance :)
Sub RECT_MBTCLeads2_Extracts()
Dim cmd As New ADODB.Command, rs As ADODB.Recordset
Dim sht As Worksheet
Dim b As String
Dim d As Long
Dim a As String
Set sht = ActiveWorkbook.Sheets("AGBA_Conversion")
sht.Range("B1").ClearContents
cmd.ActiveConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\nemberga\OneDrive - AXA\Documents\Automation\MBTC Conversion.accdb"
cmd.CommandType = adCmdText
cmd.CommandText = "AGBA_Conversion" '<<<--here is where you write your query sql
cmd.Parameters("[days]") = ActiveWorkbook.Sheets("MABI_Extracts").Range("B18").Value
Options = adCmdTable
Set rs = cmd.Execute
sht.Range("A1").CopyFromRecordset rs '--bulk copy to the Excel sheet
rs.Close
cmd.ActiveConnection.Close
MsgBox "All data were successfully retrieved from the queries!", vbInformation, "Done"
End Sub
Parameters is a collection of Parameter. You need first to create the object using CreateParameter, then to add it to the collection using Append.
Replace
cmd.Parameters("[days]") = ActiveWorkbook.Sheets("MABI_Extracts").Range("B18").Value
By
Dim p
Set p = cmd.CreateParameter("days", adInteger, adParamInput)
p.Value = ActiveWorkbook.Sheets("MABI_Extracts").Range("B18").Value
cmd.Parameters.Append p
See https://learn.microsoft.com/fr-fr/sql/ado/reference/ado-api/append-and-createparameter-methods-example-vb?view=sql-server-ver15
I need to move data from Excel sheet to database. To do this, I create ADODB Connection and I am able to execute such an SQL query:
INSERT INTO myTable SELECT * FROM [Excel 12.0 Macro;HDR=Yes;Database=C:\MyPath\MyFile.xlsb].[Shee1$A1:C100]
My problem is that the range cannot point further than 255 columns, i.e. column IU. I want to try using named range instead, but I cannot find suitable notation. All examples I found connect directly to the workbook, and use either SELECT * FROM [Sheet1$] reference, or SELECT * FROM myRange as an example of named range. I tried things like
[Excel 12.0 Macro;HDR=Yes;Database=C:\MyPath\MyFile.xlsb].[myRange]
[Excel 12.0 Macro;HDR=Yes;Database=C:\MyPath\MyFile.xlsb].[myRange$]
[Excel 12.0 Macro;HDR=Yes;Database=C:\MyPath\MyFile.xlsb].myRange
[Excel 12.0 Macro;HDR=Yes;Database=C:\MyPath\MyFile.xlsb;Name=myRange]
, but without success.
What is the proper way to use named range here? Would it even help working around column number limitation?
I expected [Excel 12.0 Macro;HDR=Yes;Database=C:\MyPath\MyFile.xlsb].[myRange] to work, but it throws the following error: 'The Microsoft Access database engine could not find the object 'myRange'. Make sure the object exists (...)'
I can work it around by copying data from source sheet to temporary one, and have it within 255 column limit, but it would be great do it right way.
Not sure if you are going to find a solution for connecting to a named range. I took a look at getting this to work, and I had no luck either, I suspect it's not included in the schema past 255 column, but could be wrong.
I thought you might as well have an efficient solution that doesn't rely on looping for adding data to Access. It's more code than just doing an insert, but I hope it fits your specific problem.
I was able to do an insert of ~2500 records (all integers) in about 3 seconds, so it is fairly quick.
Option Explicit
Private Function GetDisconnectedRecordset(TableName As String) As ADODB.Recordset
Dim conn As ADODB.connection: Set conn = getConn()
Dim rs As ADODB.Recordset: Set rs = New ADODB.Recordset
With rs
.CursorLocation = adUseClient ' <-- needed for offline processing
'Get the schema of the table, don't return anything
.Open "Select * from " & TableName & " where false", conn, adOpenDynamic, adLockBatchOptimistic
End With
rs.ActiveConnection = Nothing
conn.Close
Set conn = Nothing
Set GetDisconnectedRecordset = rs
End Function
'Do an update batch of the data
'Portion used from: https://stackoverflow.com/questions/32821618/insert-full-ado-recordset-into-existing-access-table-without-loop
Sub PopulateDataFromNamedRange()
Dim conn As ADODB.connection
Dim ws As Excel.Worksheet: Set ws = ThisWorkbook.Worksheets("Sheet2") 'Update to your sheet/wb
Dim NamedRange As Excel.Range: Set NamedRange = ws.Range("Test") ' Update to your named range
Dim NamedItem As Excel.Range
Dim rs As ADODB.Recordset: Set rs = GetDisconnectedRecordset("[TestTable]") 'Specify your table name in access
Dim FieldName As String
Dim Row As Long
Dim AddRow As Long
'Add Data to the disconnected recordset
For Each NamedItem In NamedRange
If Not NamedItem.Row = 1 Then
Row = NamedItem.Row
If Not Row = AddRow Then rs.AddNew
AddRow = NamedItem.Row
FieldName = ws.Cells(NamedItem.Row - (NamedItem.Row - 1), NamedItem.Column).Value
rs.Fields(FieldName).Value = NamedItem.Value
End If
Next
'Connect again
Set conn = getConn()
Set rs.ActiveConnection = conn
rs.UpdateBatch '<-- 'Update all records at once to Access
conn.Close
End Sub
Private Function getConn() As ADODB.connection
Dim conn As ADODB.connection: Set conn = New ADODB.connection
conn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\Ryan\Desktop\Example.accdb"
Set getConn = conn
End Function
I had the same issue and the solution is pretty easy, even though it works only for named ranges at Workbook level.
The connection has to be done to the Workbook (i.e. [Excel 12.0 Macro;HDR=Yes;Database=C:\MyPath\MyFile.xlsb]).
Then in the Query just type: SELECT * FROM [myRange] (Please note the square brackets and the absence of $ sign)
You need to use a construction:
[Excel 12.0 Macro;HDR=Yes;Database=C:\MyPath\MyFile.xlsb].[$myRange]
I have been doing some database work with excel both as the database and the database driver via macros with vba. I built a function that should parse through a list of database records with testID fields. I wanted to display each test only once based on it's testID, but the way the database is set up means I have to eliminate duplicate testID's. I do this by iterating through the recordset and checking the current test against the previous one before showing in the list. The problem I'm having is that the function is excruciatingly slow. For only 12 tests in the database, it takes about 3 seconds to display them in the view spreadsheet. I'd love to hear some ideas on how to optimize the run-time. Here's the function:
Public Function showAllTests()
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim cstring, sql As String
Dim r, c As Integer
Dim testsAr As Variant
Dim inAr As Boolean
cstring = "Provider = Microsoft.ACE.OLEDB.12.0; Data Source=I:\DBtrials.xlsx; Extended Properties=""Excel 12.0 Xml; HDR=YES;ReadOnly=False"";"
sql = "SELECT [TestID], [Status], [PFIBox], [WireType], [StartingDia], [Customer], [numSamples], [Assigned] FROM [Tests$]"
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
Call conn.Open(cstring)
Set rs = conn.Execute(sql)
If rs.EOF Then
Range("C6:J1000").ClearContents
End If
r = 6
count = 0
'Iterates through the recordset, eliminating duplicates and populating cells in the tests sheet
While Not rs.BOF And Not rs.EOF
Dim prevID, currID As String
Dim currCell As Range
inAr = False
If Not count = 0 Then
prevID = ActiveWorkbook.Sheets("Tests").Cells(r - 1, 3).Value
currID = CStr(rs(0))
If prevID = currID Then
inAr = True
End If
End If
For c = 3 To (rs.Fields.count + 2)
Set currCell = ActiveWorkbook.Sheets("Tests").Cells(r, c)
If Not IsNull(rs(c - 3).Value) And inAr = False Then
currCell.Value = CStr(rs(c - 3))
ElseIf IsNull(rs(c - 3).Value) Then currCell.Value = ""
Else:
Exit For
End If
Next c
If inAr = False Then
r = r + 1
End If
rs.MoveNext
count = count + 1
Wend
conn.Close
Set conn = Nothing
End Function
use GROUP BY
sql = "SELECT [TestID], [Status], [PFIBox], [WireType], [StartingDia], [Customer], [numSamples], [Assigned] FROM [Tests$] GROUP BY [TestID]"
Also some of these drivers - Microsoft.ACE.OLEDB.12.0 etc have terrible perfomance in VBA. sometimes i get better perforamance from OBDC 6.2 than ADO
So I have an access database with multiple tables where I store data on companies. I have designed several queries in Access to pull some information.
From Excel VBA, I connect to the Access database using ADO connection and pull the stored query. I have done so several times already and it works fine. But one of my query doesn't return any result: the recordset returns "either EOF or BOF is true".
When I open the query in ACCESS, ACCESS prompts me for the parameter "GICS_SUB_INDUSTRY_NAME", and it does returns a table with values to me if I input a parameter. So the query works fine in ACCESS but doesn't from Excel VBA.
Here is the SQL statement of the query:
PARAMETERS GICS_SUB_INDUSTRY_NAME Text ( 255 );
SELECT STOCK_STATIC.NAME, Focus_MAXHistoryDate.MaxOfHistory_Date, Focus_MAXHistoryDate.Isin, STOCK_DYNAMIC.CUR_MKT_CAP, STOCK_HIST_IS_BS_CF.BS_TOT_LIAB2, STOCK_HIST_IS_BS_CF.PREFERRED_EQUITY__MINORITY_INT, STOCK_HIST_IS_BS_CF.BS_CASH_NEAR_CASH_ITEM, STOCK_STATIC.GICS_SUB_INDUSTRY_NAME
FROM ((Focus_MAXHistoryDate INNER JOIN STOCK_HIST_IS_BS_CF ON (Focus_MAXHistoryDate.Isin = STOCK_HIST_IS_BS_CF.Isin) AND (Focus_MAXHistoryDate.MaxOfHistory_Date = STOCK_HIST_IS_BS_CF.History_Date)) INNER JOIN STOCK_STATIC ON Focus_MAXHistoryDate.Isin = STOCK_STATIC.ISIN) INNER JOIN STOCK_DYNAMIC ON Focus_MAXHistoryDate.Isin = STOCK_DYNAMIC.Isin
WHERE (((STOCK_STATIC.GICS_SUB_INDUSTRY_NAME)=[GICS_SUB_INDUSTRY_NAME]));
And this is my code in Excel VBA:
Sub Pull_Stock_Peers_Leverage()
'define general variables
Dim Connection As ADODB.Connection
Dim Command As ADODB.Command
Dim GICS_SUB_INDUSTRY_NAME As ADODB.Parameter
Dim RecordSetL As ADODB.RecordSet
Dim iField As Integer
Dim Workbook As Excel.Workbook
Set Workbook = Excel.ActiveWorkbook
Dim Start As Excel.Worksheet
Set Start = ActiveWorkbook.Sheets("Start")
Dim GICS4_Range As Excel.Range
Set GICS4_Range = Excel.Range("GICS4_LookUp")
'Open connection to the AMSIR database
Set Connection = New ADODB.Connection
With Connection
.Provider = "Microsoft.ACE.OLEDB.12.0"
.Open "N:\Switzerland\Others\_AMSIR_Company_Database\AMSIR_Company_Database.accdb"
End With
'Open command object and pass a parameter (criteria) to the query
'The parameter name should match the parameter clause in the SQL statement?
Set Command = New ADODB.Command
With Command
.ActiveConnection = Connection
.CommandText = "Pull_Peers_Leverage"
.CommandType = adCmdStoredProc
.Parameters.Append .CreateParameter("GICS_SUB_INDUSTRY_NAME", adVarWChar, adParamInput, Len(GICS4_Range))
.Parameters("GICS_SUB_INDUSTRY_NAME") = GICS4_Range
End With
'Create recordset by executing the command
Set RecordSetL = New ADODB.RecordSet
RecordSetL.CursorLocation = adUseClient
RecordSetL.CursorType = adOpenStatic
RecordSetL.Open Command
'Write fields from record set
Dim TargetRangeL As Excel.Range
Set TargetRangeL = Start.Range("PEERS_LEVERAGE")
Dim FieldCount As Integer
Dim i, j As Integer
FieldCount = RecordSetL.Fields.Count
i = 28
j = 37
For iField = 1 To FieldCount
Start.Cells(i + iField, j).Value = RecordSetL.Fields(iField - 1).Name
Next
'Transpose recordset values and paste into array
Dim recArray As Variant
Dim recCount As Long
recArray = RecordSetL.GetRows
recCount = UBound(recArray, 2) + 1
TargetRangeP.Resize(FieldCount, recCount).Value = recArray
Connection.Close
End Sub
So I do get the recordset in the code and I can actually loop into the recordset and extract field names but the fields have no value and come as "Either BOF or EOF is true or the current record has been deleted".
I have this code for all my other queries but only for this query the recordset comes empty. And it's the only query of that type I have (that does match a query and tables with inner joints).
EDIT:
I was wondering if the issue was the ADO or the way I pass my parameter so I decided to just pass the SQL statement to the ACCESS without putting any parameter; so I should normally get a recordset with all available values.
I have tried:
Sub Test1()
Dim Connection As New ADODB.Connection
Dim Command As ADODB.Command
Dim RecordSetUF As New ADODB.RecordSet
Dim Workbook As Excel.Workbook
Set Workbook = Excel.ActiveWorkbook
Dim Start As Excel.Worksheet
Set Start = ActiveWorkbook.Sheets("Start")
Dim GICS4_Range As Excel.Range
Set GICS4_Range = Excel.Range("GICS4_LookUp")
'Open connection to the AMSIR database
Set Connection = New ADODB.Connection
With Connection
' .Provider = "SQLOLEDB"
.Provider = "Microsoft.ACE.OLEDB.12.0"
' .Provider = "Microsoft.Jet.OLEDB.4.0"
.Properties("Data Source") = "N:\Switzerland\Others\_AMSIR_Company_Database\AMSIR_Company_Database.accdb"
.Open
End With
'Place sql statement and match it to newly created recordset
Set Command = New ADODB.Command
Command.ActiveConnection = Connection
Set RecordSetUF = New ADODB.RecordSet
Dim sql1 As String
'ACCESS accepts * for empty values but Excel VBA needs % as wild card in an SQL queryIf CompF.Text = "" Then CompF.Text = ""
sql1 = "SELECT STOCK_STATIC.NAME, Focus_MAXHistoryDate.MaxOfHistory_Date, Focus_MAXHistoryDate.Isin, STOCK_DYNAMIC.CUR_MKT_CAP, STOCK_HIST_IS_BS_CF.BS_TOT_LIAB2, STOCK_HIST_IS_BS_CF.PREFERRED_EQUITY__MINORITY_INT, STOCK_HIST_IS_BS_CF.BS_CASH_NEAR_CASH_ITEM, STOCK_STATIC.GICS_SUB_INDUSTRY_NAME FROM ((Focus_MAXHistoryDate INNER JOIN STOCK_HIST_IS_BS_CF ON (Focus_MAXHistoryDate.MaxOfHistory_Date = STOCK_HIST_IS_BS_CF.History_Date) AND (Focus_MAXHistoryDate.Isin = STOCK_HIST_IS_BS_CF.Isin)) INNER JOIN STOCK_STATIC ON Focus_MAXHistoryDate.Isin = STOCK_STATIC.ISIN) INNER JOIN STOCK_DYNAMIC ON Focus_MAXHistoryDate.Isin = STOCK_DYNAMIC.Isin;"
Command.CommandText = sql1
'Debug.Print sql1
'Debug.Print returns an SQL statement that works fine in Access!
Command.Execute
RecordSetUF.Open Command
So basically I have an sql statement that works fine in ACCESS, but not in excel VBA.
You were working in MS-Access, in a dialect of SQL called 'Jet-SQL'.
You are now working in Excel, with Jet-SQL, and all of the things that the MS-Access host application does for Jet are missing:
You don't have access to VBA native functions (just the Jet-SQL
native functions, which resemble some of the VBA string and
type-conversion functions)
You don't have access to VBA functions written by the Access
developer and globally-declared inside the MS-Access database;
The subqueries declared in your FROM clause had better be tables, or
queries with no VBA, too.
The asterisk wildcard won't be silently translated into '%' for you
(which I think you've got)
These things fail silently, or with misleading error messages - "Either BOF or EOF is true or the current record has been deleted" - instead of the 'missing parameter' or 'incorrect column name' errors you would expect...
But the place I'd start looking for an empty recordset and no explanation is the FROM clause: either it's one of the obvious 'No MS-Access' issues, or it's something more subtle involving a failure to match on NULL or Empty String - a failure which may well be another issue that the MS-Access wrapper manages in the background.
A quick note on Null-handling: you don't have IFNULL or NZ, so testing IIF(tbl.[Field] IS NULL is your best bet.