VBA/SQL openrecordset(strSQL) Error 3001 - sql

I am trying to run a query on a database server (Oracle 10g) using VBA/Excel and to display the results in a excel table. I have created the ODBC connection (read-only) and tested the connection. I works fine if I import data as a data source in excel (I am able to filter through a query as well) However my VBA code is giving me Error 3001
Sub Test()
Dim cnn As ADODB.Connection
Dim canConnect As Boolean
Dim rs As Recordset
Dim strSQL As String
Set cnn = New ADODB.Connection
cnn.Open "DSN=blah;Uid=blah;Pwd=blah"
strSQL = "select job_start_dttm, job_end_dttm from c_job"
Set rs = cnn.openrecordset(strSQL)
ActiveCell = rs(0)
End Sub
I get Error 3001 - Arguemnts are of worng type, are out of acceptable range, or are in confilct with one another
The query itself runs fine in SQL developer. Thanks
edit: The error is on "Set rs = cnn.openrecordset(strSQL)" line

Try:
Sub Test()
Dim cnn As New ADODB.Connection
Dim canConnect As Boolean
Dim rs As New ADODB.Recordset
Dim strSQL As String
cnn.Open "DSN=blah;Uid=blah;Pwd=blah"
strSQL = "select job_start_dttm, job_end_dttm from c_job"
rs.Open strSQL, cnn
ActiveCell = rs(0)
End Sub
You seem to be mixing up a little DAO with your ADODB. You could have used Execute, but the above should suit.

Try qualifying the type name of rs with the ADODB prefix to make sure it is not being defined as the built-in Access Recordset object type instead.
Dim rs As ADODB.Recordset
Edit:
You will also need to use the ADO .Execute command instead of the DAO .OpenRecordset:
Set rs = cnn.Execute("...")

Related

DAO passthrough query using VBA: "Error 3131 Syntax error in from clause"

I copied a solution found in Stack Overflow, adapted it to my needs.
Public Function getAssortmentTypes(Optional personId As Variant) As DAO.Recordset 'personId is integer
Dim strQuery As String
Dim qdf As DAO.QueryDef
Dim rst As DAO.Recordset
If IsMissing(personId) Then
strQuery = "SELECT assortment_type.type_id, assortment_type.type_name AS qryTest FROM assortment_type"
Else
strQuery = "SELECT * FROM get_non_deleted_assortment_types_by_user(" & personId & ")"
End If
Set qdf = CurrentDb.CreateQueryDef("")
With qdf
.SQL = strQuery
.Connect = getDBConnectionString
.ReturnsRecords = True
End With
Set rst = qdf.OpenRecordset
Debug.Print rst!qryTest
Set getAssortmentTypes = rst
End Function
In my postgresql db I do have a working function and appropriate tables. I've tested sql queries with DBEaver and they work.
I'm receiving just one row (should be about 30) when I call the function without a parameter.
With a parameter I expect filtered resultset but receive
"Error 3131 Syntax error in from clause".
Always set the connection string before setting the SQL.
When you set the SQL, DAO doesn't have a clue this will later become a passthrough query, so it tries to parse it as Access SQL, and obviously fails, since it's not valid Access SQL.
Simply change the order:
With qdf
.Connect = getDBConnectionString
.ReturnsRecords = True
.SQL = strQuery
End With
Do note that you should be using parameters, and generally, use ADO instead of DAO when working with external data sources. DAO is great with Access, but offers less features with external data sources. ADO won't try parsing the SQL string before it actually needs to, for example.
Forgo the need for DAO and QueryDefs and use ADO with command parameterization which can then be binded to a recordset:
' SET REFERENCE TO Microsoft ActiveX Data Object #.# Library
Public Function getAssortmentTypes(Optional personId As Variant) As ADODB.Recordset
Dim conn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim cmd As ADODB.Command
Set conn As New ADODB.Connection
conn.Open getDBConnectionString
' PREPARED STATEMENT WITH QMARKS ?
If IsMissing(personId) Then
strQuery = "SELECT assortment_type.type_id, assortment_type.type_name AS qryTest FROM assortment_type"
Else
strQuery = "SELECT * FROM get_non_deleted_assortment_types_by_user(?)"
End If
Set cmd = New ADODB.Command
With cmd
.ActiveConnection = conn
.CommandText = strQuery
.CommandType = adCmdText
' BIND PARAMETER
.Parameters.Append .CreateParameter("user_param", adInteger, adParamInput, , personId)
' EXECUTE QUERY AND BIND INTO RECORDSET
Set rst = .Execute
End With
Set cmd = Nothing
Set getAssortmentTypes = rst
End Function
I don't recommend the introduction of ADO.
The issue looks to be that your first SQL query would (and does) work as a linked table, and thus works because it not a pass-through query.
The 2nd sql fails, since it still try to use "access" sql, and not postgresSQL syntax.
I recommend that you create a PT query (using Access UI). In the designer, make sure you select pass-though:
So, like linked tables - put the connection string in that PT query.
Do not put or attempt to place connection strings in the code. Your re-link routines can thus also include to re-link PT queries.
You can now use this code:
Public Function getAssortmentTypes(Optional personId As Variant) As DAO.Recordset 'personId is integer
Dim rst As DAO.Recordset
Dim strQuery As String
If IsMissing(personId) Then
strQuery = "SELECT assortment_type.type_id, assortment_type.type_name AS qryTest FROM assortment_type"
Else
strQuery = "SELECT * FROM get_non_deleted_assortment_types_by_user(" & personId & ")"
End If
With CurrentDb.QueryDefs("qryPT")
.SQL = strQuery
Set rst = .OpenRecordset
End With
Debug.Print rst!qryTest
Set getAssortmentTypes = rst
End Function
So, create a PT query called (for this example) qryPT
Try omitting the “DAO.” Prefix in your Recordset and dimension statements. Later versions of Access understand what you want.

connecting to sql server via vba/odbc

I'm not sure what is wrong with my code here, it is not throwing any errors and is compiling successfully. However, the recordset is not grabbing any data. Nothing is being pasted into the sheet. The query itself runs fine from command line/sqlserver. Do I need to add a dsn somewhere in the connection string?
Sub queryTest()
Dim connection As New ADODB.connection
Dim recordset As ADODB.recordset
Dim strSQL As New ADODB.Command
connection.Open "DRIVER={SQL Server};SERVER=xxx;" & _
"trusted_connection=yes;DATABASE=xxxx"
strSQL.ActiveConnection = connection
strSQL.CommandText = "SELECT TOP (50) [CalendarSK] ,[CalendarMonthSK] ,[CalendarDate] FROM [xxxx].[dbo].[tblCalendar]"
strSQL.CommandType = adCmdText
Set recordset = strSQL.Execute
Sheets("Sheet1").Range("a1").CopyFromRecordset recordset
recordset.Close
connection.Close
End Sub

SQL Excel VBA Run-time Error 3709 Invalid Connection

This is my first question so constructive criticism is welcome! I am attempting to query an access database from excel vba and place the return information into an Excel range. I get this error:
Error Message: "Run-time error '3709' The connection cannot be used to
perform this operation. It is either closed or invalid in this
context."
Code:
Sub Importfromaccess()
Path = "C:\Users\myUser\Desktop\Database1.accdb"
Set cn = CreateObject("ADODB.connection")
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & Path & ";"
Set rs1 = CreateObject("ADODB.recordset")
rs1.activeconnection = cn
Dim strSQL As New ADODB.Command
strSQL.CommandText = "SELECT * FROM Tooling WHERE TID=BD0001"
strSQL.CommandType = adCmdText
Set rs1 = strSQL.Execute ' This is the line the error occurs on
Sheets("Calc").Range("K1").CopyFromRecordset rs1
End Sub
I have enabled the following references:
Visual Basic For Applications,
Microsoft Excel 16.0 Object Library,
OLE Automation,
Microsoft Office 16.0 Object Library,
Microsoft Access 16.0 Object Library,
Microsoft ActiveX Data Objects 2.0 Library,
I tried placing the line:
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & Path & ";"
right before the error line and received this error:
Run-time error '3705': Operation is not allowed when the object is
open.
Anybody know what my problem might be?
First (and unrelated to your error), unless you need to support clients using Windows 2000 or earlier, you should reference the highest Microsoft ActiveX Data Objects version instead of 2.0. If you're only using ADODB to interact with the database, you don't need the Microsoft Access 16.0 Object Library at all.
Second, if you already have a reference, don't create late bound objects like this:
Set cn = CreateObject("ADODB.connection")
Adding the reference early binds the type, so explicitly declare them and instantiate them using New:
Dim cn As ADODB.Connection
Set cn = New ADODB.Connection
Your connection string should be fine - where you run into problems are these 2 lines:
Set rs1 = CreateObject("ADODB.recordset")
rs1.activeconnection = cn
Executing an ADODB.Command will return the Recordset, not the other way around. Remove those 2 lines entirely. Instead of attaching the connection to the Recordset, you need to use it when you're building your ADODB.Command:
Dim strSQL As New ADODB.Command
strSQL.ActiveConnection = cn '<---Insert this.
strSQL.CommandText = "SELECT * FROM Table1"
strSQL.CommandType = adCmdText
Also, get rid of the Hungarian notation there - it's confusing as hell. An ADODB command isn't a String, so why should it be named strFoo?
You also need to clean up after yourself - don't leave your recordset and connection just hanging open when you're done with them. Call .Close when you're finished.
Finally, your SQL statement is most likely incorrect - you probably need to enclose your TID in single quotes('):
"SELECT * FROM Tooling WHERE TID='BD0001'"
It should look closer to this:
Sub Importfromaccess()
Dim Path As String
Path = "C:\Users\myUser\Desktop\Database1.accdb"
Dim cn As ADODB.Connection
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & Path & ";"
Dim query As New ADODB.Command
query.ActiveConnection = cn
query.CommandText = "SELECT * FROM Tooling WHERE TID='BD0001'"
query.CommandType = adCmdText
Dim rs1 As ADODB.Recordset
Set rs1 = query.Execute ' This is the line the error occurs on
Sheets("Calc").Range("K1").CopyFromRecordset rs1
'CLEAN UP AFTER YOURSELF:
rs1.Close
cn.Close
End Sub
You already Set rs1
How about trying something more like:
Sub Importfromaccess()
Dim strSQL As String, strPath as String
Dim cn as Object, rs1 as Object
strPath = "C:\Users\myUser\Desktop\Database1.accdb"
Set cn = CreateObject("ADODB.connection")
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & Path & ";"
Set rs1 = CreateObject("ADODB.Recordset")
strSQL = "SELECT * FROM Tooling WHERE TID='BD0001'"
rs1.Open strSQL, cn
Sheets("Calc").Range("K1").CopyFromRecordset rs1
End Sub
After some thorough rearranging I think I figured it out. I'm surprised at what changes fixed the problem but the following code works:
Dim con As New ADODB.Connection
Dim rs As ADODB.Recordset
Dim cmd As New ADODB.Command
cmd.CommandText = "SELECT * FROM Tooling WHERE TID='BD0001'"
con.Open "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=C:\Users\myUser\Desktop\Database1.accdb;"
cmd.ActiveConnection = con
Set rs = cmd.Execute
Sheets("Calc").Range("K1").CopyFromRecordset rs
rs.Close
con.Close
The final error was fixed with:
cmd.CommandText = "SELECT * FROM Tooling WHERE TID='BD0001'"
this line previously did not include single quotes around BD0001.
I also added an ActiveConnection to the Command object.
Edit: This is the simplest working version of this I could manage courtesy of all you helpful people!

Syntax error in FROM clause Excel VBA

Here is my code to retrieve data from access but I always encounter "Run-time error '-2147217900 (80040e14)' Syntax error in FROM clause"
Sub UPDATE_REGION()
Dim cnn As New ADODB.Connection
Dim rst As New ADODB.Recordset
Dim AW As Workbook
Set AW = ActiveWorkbook
Path = AW.Path
cnn_pth = Path & "\Master File.accdb"
Set cnn = New ADODB.Connection
With cnn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.Open cnn_pth
End With
Set rst = New ADODB.Recordset
sSQL = "select Package_Nb from [package_db] where [Hubs] is null"
rst.Open Source:=sSQL, ActiveConnection:=cnn, CursorType:=adOpenForwardOnly, LockType:=adLockOptimistic, Options:=adCmdTable
end subs
When i stop script to debug, I found Source value in Local Window like this: "select * from select Package_Nb from package_db where Hubs is null"
I don't know why vba auto add select * from to source like this, anyone know how to fix this?
Your options are wrong. This:
Options:=adCmdTable
should be this:
Options:=adCmdText
Since you are not passing a table name but a SQL string. (when you pass a table name it is effectively converted into a SELECT * FROM table_name statement).

VBA New Database Connection

How to change the code below to prevent what you see in the screenshot.
I am running a macro with the following code
Dim conn As ADODB.Connection
Dim rec1 As ADODB.Recordset
Dim thisSql As String
Set conn = New ADODB.Connection
Dim sConn As String
sConn = "Provider=SQLOLEDB;Trusted_Connection=Yes;Server=xyz;Database=xyz;UID=xyz;PWD=xyz"
conn.Open sConn
' this is creating multiple connections.
Set rec1 = New ADODB.Recordset
rec1.Open thisSql, conn
which runs a SQL Server query (which is around 20 lines long and contains 4 joins). Everything is fine except for the fact that after a couple times of running it my DB admin says that my query is loading up the DB too much.
Now, my query could be causing the problem, or it could be that Excel is starting to run multiple connections at once. Some evidence for this is the screenshot below and the fact that the load on the database appears to increase with time.
How do I establish a DB connection without constantly creating new connections?
Has anyone had similar problems working with Excel DB macros?
UPDATE
While the answers below were very useful (especially for someone starting out in VBA), it seems that the main reason my query was taking up load was a combination of multiple connections and having overlooked a line in my code:
With Sheets("FVols").QueryTables.Add(Connection:=rec1, Destination:=Sheets("FVols").Range("A1"))
.name = "data"
.FieldNames = True
.Refresh BackgroundQuery:=True <<<<<<<<<<<<<<<<<<<<<<<-----
End With
You only need to open the connection once. That literally means you can execute multiple queries on that one active connection. You must close the connection and free the reference (specially with ADODB) to avoid running into collisions and other connection related problems.
If you know the queries you are going to be executing you can create an array (or collection) and add queries to the queue.
While you already have an open connection to work with you can keep executing queries.
Scan through code there is not much difference between yours and mine so you should be able to see what is going on and where. Please, ask questions in the comments if anything is unclear
Sub DbConnection()
Dim cn As ADODB.Connection
Set cn = New ADODB.Connection
Dim rs As ADODB.Recordset
Dim strConn As String
strConn = "Driver={SQL Server};Server=; Database=; UID=; PWD="
cn.Open strConn
Dim queryArr, i
queryArr = Array("SELECT * FROM [MyTable]", "SELECT * FROM [MyOtherTable]")
For i = LBound(queryArr) To UBound(queryArr)
ExecuteQuery queryArr(i), cn, rs
Next i
cn.Close
Set cn = Nothing
End Sub
Private Sub ExecuteQuery(query As Variant, ByRef cn As ADODB.Connection, ByRef rs As ADODB.Recordset)
Set rs = New ADODB.Recordset
With rs
.ActiveConnection = cn
.Open CStr(query)
Sheets(1).Range("A1").CopyFromRecordset rs
.Close
End With
Set rs = Nothing
End Sub
Now, you only need to execute the DBConnection() once and all the queries you listed in the array will be executed.
Alternatively, if your queries are created at run-time you can pass it to the DbConnection() as a parameter.
Sub DbConnection(queryQueue As Collection)
Dim cn As ADODB.Connection
Set cn = New ADODB.Connection
Dim rs As ADODB.Recordset
Dim strConn As String
strConn = "Driver={SQL Server};Server=HELIUM\PRI; Database=sourcedata; UID=tabula; PWD=Tabula123!"
cn.Open strConn
For i = 1 To queryQueue.Count
ExecuteQuery queryQueue.Item(i), cn, rs
Next i
cn.Close
Set cn = Nothing
End Sub
Private Sub ExecuteQuery(query As Variant, ByRef cn As ADODB.Connection, ByRef rs As ADODB.Recordset)
Set rs = New ADODB.Recordset
With rs
.ActiveConnection = cn
.Open CStr(query)
Sheets(1).Range("A1").CopyFromRecordset rs
.Close
End With
Set rs = Nothing
End Sub
Update:
You can declare your connection as a Global Variable. Now you can run the DBConnection() as many times as you like and you will not be creating a new connection each time. Instead you will be using the global connection object.
Option Explicit
Public cn As ADODB.Connection
Sub DbConnection()
Set cn = New ADODB.Connection
Dim rs As ADODB.Recordset
Dim strConn As String
strConn = "Driver={SQL Server};Server=; Database=; UID=; PWD="
cn.Open strConn
Set rs = New ADODB.Recordset
With rs
.ActiveConnection = cn
.Open "SELECT * FROM [MyTable]"
Sheets(1).Range("A1").CopyFromRecordset rs
.Close
End With
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
Are you releasing the connection variable when you've finished with it? i.e.
Set rec1 = Nothing
The connection won't close fully if not.