I would like to have an Access VBA sub that sends an instruction to a SQL Server database to copy some data from one of the remote tables to another table (at least one of the tables is not linked to the local Access database). I want this to all happen on the server, as this is a lot of data.
I'm trying something like the following, but it returns an run-time error 3065 (Cannot execute a select query). Any insight into how to fix? Is there some reason I couldn't do this from Access?
Also, I'm using a DAO approach, but is there a better approach (ADO?)? Somewhat new to this so not always sure I understand the nuances of the different approaches.
Public Sub myTest()
Setup:
Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Set dbs = CurrentDb()
Set qdf = dbs.CreateQueryDef("")
qdf.SQL = "INSERT INTO tmp SELECT [Applicant], [CaseName], [DecisionDate], [Filed], [Docket] " _
& "FROM Cases WHERE [DecisionDate] >= '01/01/2018';"
qdf.Connect = "ODBC;Driver={SQL Server};server=myServer;database=myDB;"
qdf.Execute
End Sub
What you have looks ok. I would “test” the sql first by running the exact same command by using SQL management studio.
As for your code? It also looks ok, but I find it MUCH better to save a PT query and thus you don’t have to mess with the connection string in code. And thus your code becomes:
With CurrentDB.querydefs("MyPassR")
.SQL = "INSERT INTO tmp SELECT [Applicant], [CaseName], [DecisionDate], [Filed], [Docket] " & _
"FROM Cases WHERE [DecisionDate] >= '01/01/2018';"
.ReturnsRecords = False
.Execute
End With
Related
My ultimate goal is to run sql queries against sql-server and capture the returned data in a spreadsheet. The following code roughly reflects my current set-up and it works. The design allows me to read sql codes from text files and submit it to a sql-server. "Sub ExecuteCRUD" submits a first sql script to prepare data and dumps the result into a temp table. "Function loadRecordset" submits a relatively simple select query and captures the returned data in a recordset, which I then use to populate a spreadsheet.
There are a couple "variables" in my setup that could potentially be relevant for discussion.
My 4 set of Sql Codes
The ConnectionString (Part of my vba code)
The rest of my vba codes
a. Dbo.ConnectionString = "Provider=MSDASQL;DRIVER=SQL
Server;SERVER=myserver;UID=id;PWD=password;DATABASE=database;"
b. Dbo.ConnectionString = "Provider=SQLOLEDB;Data
Source=myserver;Initial Catalog=database;User
ID=id;Password=password;"
Initially, all I changed was the connection string. The immediate result was connection string version a works perfectly. Using b version, my setup would fail without any errors from sql-server.
Using Connection string version b, "Sub ExecuteCRUD" (data preparation step) would still work smoothly. I can verify that the temp table is created in tempdb as a result of my first sql script.
"Function loadRecordset" would run through the lines without any errors up to and including "rs.open". (I checked for errors, none whatsoever from the ado connection).
Only subsequent codes, when using the recordset to copy out the data would get an error: "Operation is not allowed when the object is closed."
Through some testing, I narrowed down the issue to the sql codes, sort of.
I have to reiterate here. My initial set of sql codes worked completely fine when using the ODBC provider. Things only went sideways using the OLEDB provider.
Using the OLEDB provider, the "offending" sql code was Use databaseABC. Furthermore, using ado, my setup submits 4 sets of sql codes to the sql server. The first set of sql codes prepares data (creating tables, inserting data, creating index, using while loops to populate data, using recursive ctes, etc). In this first set of sql codes, Use databaseABC was also included, and it would execute successfully. The other 3 set of sql codes submitted were only select queries aimed at obtaining data. When Use databaseABC was included in the select query sql codes, the operation failed without any errors. After I took out the Use databaseABC, everything would run correctly in the OLEDB provider world.
Use databaseABC is not a necessary part of the select queries. Using it saves me the trouble of specifying database names all the time in the join clauses.
At this point, my curiosity is two fold
why Use databaseABC causes failures, specifically only when using OLEDB provider, more specifically only when running select queries.
When the failure occurred, should sql-server or the driver generate any errors? What would be the proper way of checking and verifying that?
Private Dbo As New ADODB.Connection
Private rs As ADODB.Recordset
Public Sub ConnectServer()
If Dbo.State = adStateClosed Then
Dbo.ConnectionString = "Provider=SQLOLEDB;Data Source=*server*;" _
& "Initial Catalog=*database*;User ID=*id*;Password=*pwd*;"
Dbo.Open
End If
End Sub
Public Sub ExecuteCRUD(ByVal Sql As String)
On Error GoTo PANIC
Dbo.Execute Sql
Exit Sub
PANIC:
Debug.Print Err.Descript
Stop
End Sub
Public Function loadRecordset(ByVal Sql As String) As Long
On Error GoTo PANIC
Set rs = New ADODB.Recordset
With rs
.CursorLocation = adUseClient
.CursorType = adOpenStatic
.LockType = adLockReadOnly
.Source = Sql
Set .ActiveConnection = Dbo
End With
rs.Open
loadRecordset = rs.RecordCount
Exit Function
PANIC:
Debug.Print Err.Description
loadRecordset = 0
Stop
End Function
I'm trying to create in MS Access a pass trough query which will be connected to SQL server and use combo box from the form as a filter parameter in the WHERE statement part.
I know that connection and everything works because if I enter
SELECT * FROM mrch.Promo_Request_Base
I receive all the results.
Now when I try to enter something like
SELECT * FROM mrch.Promo_Request_Base WHERE mrch.Promo_Request_Base.Requestor_Name = 'UserABC';
then it also works.
It does not work for me if I enter SQL like this:
SELECT *
FROM mrch.Promo_Request_Base
WHERE (((mrch.Promo_Request_Base.Requestor_Name) = [Forms]![f_PromoRequest_VIEW_Header_001a]![Combo133]));
I also tried this:
SELECT *
FROM mrch.Promo_Request_Base
WHERE (((mrch.Promo_Request_Base.Requestor_Name) = [Forms]![f_PromoRequest_VIEW_Header_001a]![Combo133].Columns(0)));
[Combo133] has value 'UserABC' in it.
I would be very thankful if you could help me.
You don't.
Pass-through queries are passed to the data source as-is, and aren't parsed by Access at all. This means there's no way to use form-based parameters in a pass-through query.
Instead, use VBA to set parameters.
For how to do that, see How do I use parameters in VBA in the different contexts in Microsoft Access?. Specifically, the section on DAO applies to pass-through queries.
This also means you can't open the query for displaying. Use a form in datasheet view using VBA to set its own recordset instead. Do note that if a recordset requires parameters to be requeried, this can lead to trouble on sort/filter.
A pass-through query means it is executed server side. That server can no more go look into access and pull data then go and try and steal all your emails or financial data on your computer.
the simple solution then is to "process" or "evaluate" the expression BEFORE you send it to sql server. You can use the following:
Dim strSQL As String
strSQL = "SELECT * From mrch.Promo_Request_Base " & _
"WHERE mrch.Promo_Request_Base.Requestor_Name = '" & _
[Forms]![f_PromoRequest_VIEW_Header_001a]![Combo133] & "'"
With CurrentDb.QueryDefs("qryPass")
.SQL = strSQL
End With
' now code here to open form, or say launch report
DoCmd.OpenReport "rptPromoList", acViewPreview
Note that you have to ensure that the sql is formatted correctly for the server side.
So, in above, I am assume Requestor_Name is a text type of field. So,, that means you have to place quotes (I used single) around the expression. If that column you get from the combo box is a number, then the quotes are not required, and you would use this:
strSQL = "SELECT * From mrch.Promo_Request_Base " & _
"WHERE mrch.Promo_Request_Base.Requestor_Name = " & _
[Forms]![f_PromoRequest_VIEW_Header_001a]![Combo133]
So the other code would be the same - the only change in above was how I left out the adding of quotes (') around the expression.
Many thanks for your help.
At the end I did it differently. Im changing the PassTrough query via VBA :
Private Sub Command159_Click()
Dim db As dao.Database
Set db = CurrentDb
Dim qdf As dao.QueryDef
Set qdf = db.QueryDefs("q_PassThrough_VIEW_001a")
qdf.SQL = "SELECT * From mrch.Promo_Request_Last_Version_rpt_v " & _
"WHERE mrch.Promo_Request_Last_Version_rpt_v.F_Qtr_CD LIKE '" & _
[Forms]![f_PromoRequest_VIEW_Header_001a]![Combo145] & "%'"
qdf.Close
db.Close
Set qdf = Nothing
Set db = Nothing
End Sub
I get error 3035: 'System Resource Exceeded' when executing the following sub:
Private Sub delete_result_staging()
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Set db = CurrentDb
Set qdf = db.QueryDefs("qryWQPassthrough")
qdf.Connect = Me.con_str.Value
qdf.ReturnsRecords = False
qdf.SQL = "TRUNCATE TABLE tblWQResultStaging;"
qdf.Execute
Set qdf = Nothing
Set db = Nothing
End Sub
I am using MS Access 2013 and SQL Server 2012.
This code has worked previously; it stopped working when I refactored another sub per How to increase performance for bulk INSERTs to ODBC linked tables in Access?. I have changed the MaxLocksPerFile to 1,000,000 in the registry per other suggestions on the internet. I restarted Access and my computer, and I still get the error every time the sub fires. In the SQL Server Profiler I see no activity on the server when the code is executed. Forms and reports that point to linked tables on the same server but are connected via the GUI, rather than through VBA, still work as expected.
I figured out the issue was that I was reusing a single passthrough query multiple times in VBA. It didn't like the SQL statement that was in the query from the last time I used it in code, so it threw the error. Looks like I need to either be more careful with my earlier VBA code that uses that query, or use separate queries for every VBA sub, or both.
I'm operating on a virtual machine where my SQL server and MS Access sit. I set up an ODBC connection from Access to SQL and linked a few tables. I can perform the usual operations on these tables (select/update etc.).
BUT, I'm unable to run a stored procedure for some weird reason! The procedure runs perfectly on SSMS but not when I call it from Access VBA. Following is the code I'm using to execute the proc (I need to pass 3 parameters as well, but I've excluded that from the code below for simplicity):
With CurrentDb.QueryDefs("qPass")
.SQL = "exec [HS].[spGetXMLExtract]"
.Execute
End With
The error returned by MS Access is
Invalid SQL statement: expected 'DELETE', 'INSERT', 'PROCEDURE', 'SELECT', or 'UPDATE'.
Don't know if this is happening because:
1. I've got these applications on a VM; and/or
2. I need to somehow link the stored procedure to MS Access just like I did for the tables (perhaps this can't be done).
I guess there are convoluted methods I 'might' be able to adopt but I don't want to. Example:
1. Create a table in SQL with columns that store the parameters; Write an AFTER UPDATE Trigger on that table which executes my stored procedure; Fire an 'Update' query from MS Access that would update the parameters in that table and a SQL trigger then gets fired. OR;
2. Eliminate the stored procedure from the equation completely and execute it's individual statements (select/update/insert etc.) through a Sub in MS Access. Don't know if this would cause problems with creating temp tables though.
Can someone please advise on this, this is a real blocker!
Thanks in advance!
You will receive that error message if the QueryDef does not have a valid "ODBC;..." connection string as its .Connect property. That is how Access identifies the QueryDef as a pass-through query.
If you already have an ODBC linked table defined, you can use its .Connect property value for the .Connect property of the QueryDef, like so:
Dim cdb As DAO.Database
Set cdb = CurrentDb
Dim qdf As DAO.QueryDef
Set qdf = cdb.CreateQueryDef("")
qdf.Connect = cdb.TableDefs("dbo_table1").Connect ' grab .Connect string from linked table
qdf.sql = "exec [HS].[spGetXMLExtract]"
qdf.ReturnsRecords = False
qdf.Execute
... or, if the stored procedure does in fact return a result set:
Dim cdb As DAO.Database
Set cdb = CurrentDb
Dim qdf As DAO.QueryDef
Set qdf = cdb.CreateQueryDef("")
qdf.Connect = cdb.TableDefs("dbo_table1").Connect ' grab .Connect string from linked table
qdf.sql = "exec [HS].[spGetXMLExtract]"
qdf.ReturnsRecords = True
Dim rst As DAO.Recordset
Set rst = qdf.OpenRecordset(dbOpenSnapshot)
Do Until rst.EOF
' do stuff
rst.MoveNext
Loop
rst.Close
I do not have access right now to MS-Access, but it seems to me that the good syntax is:
`strSQL = "exec [HS].[spGetXMLExtract]"
With CurrentDb.QueryDefs(strSQL)
'Fill parameters
.Parameters(0) = My first param
.Parameters(1) = My 2nd param
.Parameters(2) = My last param
'Execute the query
.Execute
End With `
I hope that will help !
I know this will be a common problem, nevertheless I didn't find any solution.
I migrated my access database into SQL Server Express. SELECT and UPDATE work well, actually better that with the Access engine. But I can't get standard insertions running...
This does not work:
Set rc = CurrentDb.openRecordset("SELECT * FROM DBLog WHERE false")
rc.AddNew
rc.update '-->ERROR
rc.close
This does not work either:
DoCmd.RunSQL("INSERT INTO [test2].dbo.DBLog (type) VALUES (6)")
This does work:
sending the above SQL with pass through. So its not the SQL Servers problem.
I have set IDENTITY and PRIMARY in the Database. Access also knows the primary. In design view although there is no "Autonumber", and this may be the problem. But how to resolve that?
As a general rule, after migrating to SQL server for the back end, then a typical and common open reocrdset of
Set rst = CurrentDb.OpenRecordset("Contacts")
Needs to become
Set rst = CurrentDb.OpenRecordset("Contacts", dbOpenDynaset, dbSeeChanges)
So, in your case:
Set rc = CurrentDb.openRecordset("SELECT * FROM DBLog WHERE false" dbOpenDynaset, dbSeeChanges)
Since you have a linked table named DBLog, I would execute an INSERT statement, similar to the one which worked in SQL Server itself, against that linked table.
Dim db As DAO.Database
Dim strInsert As String
strInsert = "INSERT INTO DBLog ([type]) VALUES (6)"
Set db = CurrentDb
db.Execute strInsert, dbFailonerror
I enclosed the field name type in square brackets because it is a reserved word.