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

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%');

Related

VBA Unspecified Error When Querying PostgreSQL View

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.

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.

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.

Connect to a linked table

Connect to a linked table with code.
I have some linked tables from a SQL-server; they are linked with an ODBC connection. The password is not saved with the connection. When I am double clicking on the table in Access table-view I get a prompt for username and password. After entering the password I can view the data in the table.
My problem is when I try to access the table with code before having opened it in this way. What I try to do is to use ADODB to open a recordset with data from the linked table, like:
Dim rst as new ADODB.Recordset
Dim sql as string
Sql = “SELECT * FROM LinkedTable”
rst.Open sql, CurrentProject.Connection, adOpenForwardOnly, adLockReadOnly
Running this code without having access the table before will generate this error: Error# -2147467259, ODBC: connection to dns-name failed.
So, my question is, are there any way to connect to the database with code that can be run when the database is opened? This would also help the users as they would not have to remember a password to the SQL-server.
It seems that you are mixing 2 technologies that might not work together, ie linked tables through ODBC and ADODB recordsets. Have you tried to open DAO recordsets on your linked tables?
Dim rst as DAO.Recordset
Dim sql as string
Sql = “SELECT * FROM LinkedTable”
set rst = currentDb.openRecordset(sql,<your parameters>)
You could of course use ADODB recordsets through 2 ADODB connections, one to your access file, the other one to your SQL server:
Dim rsSQL as ADODB.recordset, _
rsACCESS as ADODB.recordset, _
connectionSQL as ADODB.connection, _
connectionACCESS as ADODB.connection
set connectionSQL = New ADODB.connection
set connectionACCESS = New ADODB.connection
connectionSQL.properties(...) = enumerate your SQL parameters
connectionACCESS.properties(...) = enumerate your ACCESS parameters (use currentproject.accessConnection if your access tables are local tables only)
set rsSQl = New ADODB.recordset
set rsACCESS = New ADODB.recordset
rsSQL.open "SELECT * FROM ...", connectionSQL, <other parameters>
rsACCESS.open "SELECT * FROM ...", connectionACCESS, <other parameters>
Linking ADO recordsets to forms and comboboxes in Access is possible. But, when creating forms, this technology has to be mainly managed through VBA code (you will have to write 'on open' events such as set me.recorset = ...), while the standard "linked tables" technology can be easily used through the user-friendly 'form-design' interface.
You can use a connection string in your code, it is easy enough, seeing you are already using ADO: http://www.connectionstrings.com/
You will need to find out which version of SQL Server you are linking to.

SQL Query Run Against Excel Workbook Returns Truncated Text Field

I'm running a SQL SELECT query through an ADO connection to an Excel 2007 workbook with the following code (using a custom version of VBScript)
dim ado, rs
set ado = CreateObject("ADODB.Connection")
ado.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=workbook.xlsx;Extended Properties=""Excel 12.0 Xml;HDR=YES;IMEX=1"";"
ado.open()
set rs = ado.execute("SELECT * FROM [sheet1$]")
which is straighforward. The problem is that any cell that has text longer than 255 characters is truncated; is there any way around this? Is there a property in the connection string that will support this or is it an option I need to change in the excel document itself? I have tried MSSQL's CAST() function but this just causes an error when executed.
Any help would be greatly appreciated.
I think you're running into a variant of a long-standing limitation in Excel's data access provider. See http://support.microsoft.com/default.aspx?scid=kb;EN-US;189897 for an example or google for thousands more.
Instead of trying to use CAST(), have you tried to use the CONVERT() function?