VBA Unspecified Error When Querying PostgreSQL View - vba

I am querying a PostgreSQL view in VBA:
Sub GetData()
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Set cn = New ADODB.Connection
Set rs = New ADODB.Recordset
Dim wb As Workbook: Set wb = ThisWorkbook
With cn
.ConnectionString = "Driver={PostgreSQL ANSI(x64)};Database=a;Server=b;Uid=c;Pwd=d;Port=5432;sslmode=require;"
.Open
End With
SqlString = "SELECT * FROM myView;"
rs.Open SqlString, cn
...
End Sub
The query SELECT * FROM myview; executes as expected from within pgAdmin. Within VBA, it throws an Unspecified Error. I have used the same VBA code with other simple SQL queries against the same database and it's worked properly.
The view should return 8 columns. If I list those 8 columns in my query in VBA (instead of SELECT *...), the same Unspecified Error is returned.
However, if I leave off one specific column (accountcode, which is text), and instead just return the other 7, it executes properly.
What could be the issue with this one column that's causing it to work properly in pgAdmin but not in VBA?
Thank you.

Seems the problem is the (ODBC? see if it has configuration options that would be useful for this) driver making wrong assumptions about handling the TEXT data type.
Not familiar with postgresql in any way, but according to this post about casting from TEXT to VARCHAR, consider explicitly listing each column you're selecting from the view (SELECT * is bad practice anyway), and then you can do something like this:
Dim sql As String
sql = "SELECT Field1, Field2, ThatTextField::varchar FROM myView;"
Also consider moving the connection and query code over to a class module, where your ADODB connection can be declared as a module-level WithEvents variable; then you can handle ADODB connection events like WillConnect, WillExecute, and InfoMessage, which can give you more meaningful error messages.

Related

Populate results from SQL Server using Recordset in MS Access using VBA code

I am not sure how I can put the results from Record Set into a query or result pane in MS Access using VBA code. The results in the recordset are from SQL Server, so I want to display the results in MS Access. I need to do it this way, is that possible? I would think I need to do something where are x's are.
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim strConnString As String
strConnString = "Provider=SQLNCLI11;Server=SRV;Database=Staging;Trusted_Connection=yes;"
Set conn = New ADODB.Connection
conn.Open strConnString
Set rs = conn.Execute("Select * from MSAccess_APP_ComplianceDashBoard ")
XXXXXXXXXXXXXXXXXX
rs.Close
Assuming a continues form,or a multiple items form?
In the forms on-load event just go:
me.ReocrdSource = "Select * from MSAccess_APP_ComplianceDashBoard"
It is of course assumed that you have a linked table to SQL server with the above name.
In fact, you can set the forms record source in design mode and as a result you need zero lines of code.
So, setup a linked table - you not need to write any code.
Create a pass-through query, put your connection string there, and do whatever you your needs over this query.

VBA SQL Connection and Query

I try to make connection with my database through VBA because using pivot table connection takes like 2 minutes (connecting and importing the data). In another job i had the vba code to connect to databse and do mdx query and it was much faster then by using excel pivot table. In this case i can't have mdx query because its not available (different database?).
I have found the code to connect and do query. The problem is i dont get any information if im connected or not and the query part gives me error: (run time error '-2147217865 (80040e37) Automation Error).
Here is the code:
Sub SQL_Connection()
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Dim query As String
Set con = New ADODB.Connection
Set rs = New ADODB.Recordset
Dim strCon As String
'http://learnexcelmacro.com/wp/2011/11/sql-connection-string/
strCon = "Provider=SQLOLEDB.1;Data Source=sql2\bbqsrv;Initial Catalog=Reports;Integrated Security=SSPI"
'--- Open the above connection string.
con.Open (strCon)
con.CommandTimeout = 120 'sec
'--- Now connection is open and you can use queries to execute them.
'--- It will be open till you close the connection
'slq query
query = "SELECT TOP 10 * FROM [Reports]"
'Performs the actual query
rs.Open query, con
'Dumps all the results from the query into cell A2 of the first sheet in the active workbook
Sheets(1).Range("A2").CopyFromRecordset rs
End Sub
First, can you guys help me how to check if im acutally connected and this part of code works? In excel if i check in DATA>Connections there is nothing.
Second, how to write simple query if i have only pivot table acces not database/sql? On this pivot i cant check the mdx query.

Editing & Running an SQL Query in an Access database from Excel VBA

I am trying to write a Excel VBA macro that runs a query based upon a variable in the spreadsheet. The existing data is half in a large data base (MS Access) on the network.
For some reason when my code runs, nothing is pasted back to my spreadsheet. Can you see why this may be the case:
Sub test()
Dim cnn As New ADODB.Connection
Dim rst As New ADODB.Recordset
Dim ConnectionString As String
Dim StrQuery As String
ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;DataSource=\\network\data07\version6.mdb;"
cnn.Open ConnectionString
cnn.CommandTimeout = 900
StrQuery = "SELECT dbo_vwData_SelectAll.BusinessDate, , dbo_vwData_SelectAll.Flowdate, dbo_vwData_SelectAll.Bucket FROM dbo_vwData_SelectAll WHERE (((dbo_vwData_SelectAll.Line)=""1.1.1.4"") AND ((dbo_vwData_SelectAll.ReferenceID) Like ""rent*"");"
rst.Open StrQuery, cnn
Sheets("Sheet2").Range("A2").CopyFromRecordset rst
End Sub
The above is a test - I will be changing "rent*" for other variables based on strings in the spreadsheet.
Thanks in advance.
The misappropriated LIKE operator is a known issue in the MS Access SQL dialect. When using MSAccess.exe GUI program, LIKE uses the asterisk wildcard, *. However, using MS Access via ODBC as a backend database as you are with Excel, LIKE uses the ANSI wildcard, %. See MS Office Support docs on Access wildcard character reference.
Therefore, consider the following adjustment which includes use of single quotes:
SELECT dbo_vwData_SelectAll.BusinessDate,
dbo_vwData_SelectAll.Flowdate, dbo_vwData_SelectAll.Bucket
FROM dbo_vwData_SelectAll
WHERE (((dbo_vwData_SelectAll.Line)='1.1.1.4')
AND ((dbo_vwData_SelectAll.ReferenceID) LIKE 'rent%');
To be consistent in either frontend or backend interfaces, consider the ALIKE operator:
AND ((dbo_vwData_SelectAll.ReferenceID) ALIKE 'rent%');

Can I create a pass-through query via VBA to call a parameterized tSQL UDF and send it a dynamic parameter to return results to Access?

I currently have a SQL 2008 R2 database backend with an Access 2013 accdb front end with ODBC DSN-less connection and linked tables. In SQL I have many parameterized tSQL UDFs created to feed data into reports (currently working well in my Access 2010 adp frontend). The reports are complicated: multiple tSQL UDFs run calculations and then feed into a final UDF that feeds the respective report. I would like to keep the UDFs on the server – rewriting into Access queries would be a poor solution.
My problem is that I have not been able to figure out how to write the VBA correctly to send a pass-through query to call the tSQL UDF and give it a parameter, which would change for each report. I know pass-through queries are read-only, that’s ok. I’ve read that I can call a stored procedure (SP) from VBA, but can I call the UDF rather than having to convert each to a SP or create a SP just to call the UDF so that I could call the SP from VBA. Based on my research, I think I might have to either create a SP to call the UDF or convert the UDF to a SP to get the VBA to work (i.e., return results without error). Is this correct?
I found this discussion: https://social.msdn.microsoft.com/Forums/office/en-US/898933f5-73f9-44e3-adb9-6aa79ebc948f/calling-a-sql-udf-from-access?forum=accessdev , but it has conflicting statements “You can't call a tSql udf from Access.”, and “You can use a passthrough query to call UDF's or stored procedures or anything else written in tsql.” Also, their code is written in ADO instead of DOA so it’s a bit cryptic to me since I’ve only written DAO so far, but the general gist that I got was they converted their UDF to a SP.
I found this article a great read, but again did not get a clear “yes” to my question:
http://technet.microsoft.com/en-us/library/bb188204(v=sql.90).aspx
It may be possible to remove the parameter from the Server side and add it to the Access side similar to this Access 2007 forms with parameterized RecordSource , but wouldn't that cause Access to load the entire dataset before filtering, instead of processing on the Server side – possibly causing performance issues?
I can successfully create a pass-through query in the Access interface if I supply it with a constant parameter, for example “Select * from udf_FinalReport(2023)”, but what I really need is to be able to pass a dynamic parameter. For example, the parameter would be from Forms!Control![txtboxValue]. Can I do this? The following code is what I’m using– it works if I use a table name in the SQL (ex, “SELECT * FROM Table WHERE tblYear = “&intYear ) in line 9 so I feel like I have everything coded right, but when I put my UDF in the SQL like below I get the error #3131 “Syntax error in FROM clause.” (I did verify that I should not use the prefix schema (dbo.) – this gives error 3024 “could not find file”.) Is this user error or just plain telling me I can’t call a UDF this way?
1 Sub AnnualSummary()
2 Dim dbs As DAO.Database
3 Dim qdfPoint As DAO.QueryDef
4 Dim rstPoint As DAO.Recordset
5 Dim intYear As Integer
6 intYear = Reports!Annual_Delineation_Summary!txtYear
7 Set dbs = OpenDatabase("", False, False, "ODBC;DRIVER=sql server;SERVER=******;APP=Microsoft
8 Office 2010;DATABASE=*******;Network=DBMSSOCN")
9 Set qdfPoint = dbs.CreateQueryDef("", "Select * from udf_AnnualReport(" & intYear& ")")
10 GetPointTemp qdfPoint
11 ExitProcedure:
12 On Error Resume Next
13 Set qdfPoint = Nothing
14 Set dbs = Nothing
15 Set rstPoint = Nothing
16 Exit Sub
17 End Sub
18
19 Function GetPointTemp(qdfPoint As QueryDef)
20 Dim rstPoint As Recordset
21 With qdfPoint
22 Debug.Print .Name
23 Debug.Print " " & .SQL
24 Set rstPoint = .OpenRecordset(dbOpenSnapshot)
25 With rstPoint
26 .MoveLast
27 Debug.Print " Number of records = " & _
28 .RecordCount
29 Debug.Print
30 .Close
31 End With
32 End With
33 End Function
I also tried writing the code a little differently, using the following instead of lines 5, 6, and 9. This also works when I use a table name in the select statement, but I get error #3131 when I use a UDF name:
Set qdfPoint = dbs.CreateQueryDef("", "Parameters year int; Select * from Point_Info where
year(Sample_Date) = year")
qdfPoint.Parameters("year").Value = intYear
Both code variations also work if I try use the name of a SQL View in the tSQL SELECT statement.
My consensus is using ADO language instead of DAO to write the pass-through query works well. But, I have found that it is still probably better to execute a stored procedure than to try to call the UDF. Here is the code that ended up working most smoothly for me: (my ADO Connection uses Public variables strUID and strPWD)
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim strPoint As String
strPoint = Forms!FRM_Vegetation_Strata!Point_ID
Set cn = New ADODB.Connection
cn.Open "Provider = sqloledb;Data Source=imperialis.inhs.illinois.edu;" & _
"Initial Catalog=WetlandsDB_SQL;User Id=" & strUID & ";Password=" & strPWD
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = cn
.Source = "sp_Report_VegWorksheet '" & strPoint & "'"
.LockType = adLockOptimistic
.CursorType = adOpenKeyset
.CursorLocation = adUseClient
.Open
End With
Set Me.Recordset = rs
On a side note I found that to get set the .Recordset to fill a subform put this code in the "Open" event of the subform.
Then to clean up your connection:
Private Sub Form_Unload(Cancel As Integer) 'use "unload", not "close"
'Close the ADO connection we opened
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Set cn = Me.Recordset.ActiveConnection
cn.Close
Set cn = Nothing
Set rs = Nothing
Set Me.Recordset = Nothing
End Sub
This approach does not work for populating a report. "Set Me.Recordset" only works for forms. I believe I will have to call a stored procedure then populate a temp table to use as the report recordset.
EDIT: I have found that I can call a SQL UDF or SP from VBA in Access using DOA. This is particularly helpful when one wants to pull the data from a complicated SQL function/procedure and put it into an Access-side temp table. See Juan Soto's blog https://accessexperts.com/blog/2012/01/10/create-temp-tables-in-access-from-sql-server-tables/#comment-218563 This code puts the info into a temp table, which is what I wanted to populate my reports. I used his code example and the following to call the sub:
To execute as SP: CreateLocalSQLTable "testTBL","exec dbo.sp_Report_WetDet_point '1617-1-1A'",False
To call a UDF: CreateLocalSQLTable "testTBL","Select * from dbo.QryReport_Main('1617-2-2A')",False
I don't know if it's the most efficient method of passing a variable parameter through a pass-through query into a function and returning the results to Access, as I am still relatively new to Access, but I came across this earlier when I was attempting a similar problem.
I managed it by creating a couple of pass-through queries that executed functions in SQL server and returned a result. I then made a small VBA script that re-wrote the pass-through queries with the new variable every time I wanted to change it, and executed them.
I got the result back out using OpenRecordset, and stored it as a string to use in the rest of my code.

Execute Query from Access via Excel Query in VBA

Access has saved a query that was designed with the query builder called 'myQuery'. The database is connected to the system via ODBC connection. Macros are all enabled.
Excel Has makes a ADODB connection to connect to the database via
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Set con = New ADODB.Connection
With con
.Provider = "Microsoft.ACE.OLEDB.12.0"
.Open "MyDatabase.accdb"
End With
Usually you would go ahead and just write your SQL, which is perfectly fine and then just do something like
Dim sqlQuery As String
sqlQuery = "SELECT * FROM myTable"
Set rs = New ADODB.Recordset
rs.Open sqlQuery, con, ...
But I want to access the query that I saved in the access database. So how do I call the saved query in the database that I just connected.
Tried already
con.Execute("EXEC myQuery") but that one told me it could not be find myQuery.
rs.Open "myQuery", con but that one is invalid and wants SELECT/etc statements from it
I think you can treat it like a stored procedure.
If we start right before Dim sqlQuery As String
Dim cmd as new ADODB.Command
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "myQuery"
cmd.ActiveConnection = con
Set rs = cmd.Execute()
Then pickup your recordset work after this.
You were nearly there:
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Set con = New ADODB.Connection
With con
.Provider = "Microsoft.ACE.OLEDB.12.0"
.Open "z:\docs\MyDatabase.accdb"
End With
con.Execute "MyQuery"
Just leave out Exec.
You can add parameters, too, this is a little old, but should help: update 2 fields in Access database with Excel data and probably a Macro
I was able to run an update query that was already saved in Access using:
Connection.Execute "My_Update_Query_Already_Saved_In_Access", adExecuteNoRecords, adCmdStoredProc
This gave me errors until I replaced spaces in the query name with underscores in both the Access database and the execute statement.
This is sort of a hack job, but you can query a query. That is, replace your sql string with the following:
sqlQuery = "SELECT * FROM QueryName;"
Before running this, one must ensure that the Access Database has been saved ie. press Ctrl+S (it is not sufficient that the query was run in Access).
Long time since this thread was created. If I understand it correctly, I might have something useful to add. I've given a name to what the OP describes, that being the process of using SQL from a query saved in an ACCDB to run in VBA via DAO or ADOBD. The name I've given it is "Object Property Provider", even with the acronym OPP in my notes, and for the object name prefix/suffix.
The idea is an existing object in an ACCDB (usually a query) provides a property (usually SQL) that you need to use in VBA. I slapped together a function just to suck SQL out of queries for this; see below. Forewarning: sorry, but this is all in DAO, I don't have much use for ADODB. Hope you will still find the ideas useful.
I even went so far as to devise a method of using/inserting replaceable parameters in the SQL that comes from these OPP queries. Then I use VBA.Replace() to do the replacing before I use the SQL in VBA.
The DAO object path to the SQL of a query in an ACCDB is as follows:
mySqlStatement = Access.Application.CurrentDb.QueryDefs("myQueryName").SQL
The way I use replaceable parameters is by evaluating what needs to be replaced, and choosing an unusual name for the paramater that cannot possibly exist in the real database. For the most part, the only replacements I've made are field or table names, or the expressions of WHERE and HAVING clauses. So I name them things like "{ReplaceMe00000001}" and then use the Replace() function to do the work...
sqlText = VBA.Replace(sqlText, "{ReplaceMe00000001}", "SomeActualParameter")
...and then use the sqlText in VBA. Here's a working example:
Public Function MySqlThing()
Dim sqlText as String
Dim myParamater as String
Dim myExpression as String
'Set everything up.
sqlText = getSqlTextFromQuery("myQuery")
myParameter = "{ReplaceMe00000001}"
myExpression = "SomeDateOrSomething12/31/2017"
'Do the replacement.
sqlText = VBA.Replace(sqlText, myParameter, myExpression)
'Then use the SQL.
db.Execute sqlText, dbFailOnError
End Function
Function getSqlTextFromQuery(ByVal oppName As String) As String
Dim app As Access.Application
Dim db As DAO.Database
Dim qdefs As DAO.QueryDefs
Dim qdef As DAO.QueryDef
Dim sqlText As String
Set app = Access.Application
Set db = app.CurrentDb
Set qdefs = db.QueryDefs
Set qdef = qdefs(oppName)
oppGetSqlText = qdef.SQL
End Function