Excel VBA to Execute Stored UNION Query from MS Access 2003 - vba

I'm writing a function in Excel VBA to execute a stored queries on a database created using MS Access 2003. The code works when the stored query is a simple query, but NOT if the query is a UNION. For example, I'm using the following code to execute the stored queries:
Public Function QueryDB()
Dim cn As Object
Dim strConnection As String
Set cn = CreateObject("ADODB.Connection")
' Hard code database location and name
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\\server1\myDatabase.mdb"
' Open the db connection
cn.Open strConnection
' Create call to stored procedure on access DB
Dim cmd As Object
Set cmd = CreateObject("ADODB.Command")
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "testQuery"
cmd.ActiveConnection = cn
' Execute stored query
Dim rs As ADODB.Recordset
Set rs = cmd.Execute()
MsgBox rs.Fields(0) ' prints as expected
QueryDB2 = rs.Fields(0)
' close connections
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Function
If testQuery is a simple query (e.g., does not use UNION), then data is returned as expected. However, if the testQuery contains a UNION, then the Excel VBA code fails (and no particular error is returned). How can I work-around this issue? I'd like to avoid writing the SQL statement in VBA.

Consider using ADO's Open Recordset method. Usually, Execute are for action commands (append/update/delete, stored procedures, etc.). Additionally, if Execute returns a recordset it is only a forward-only (i.e, snapshot with no cursor), read-only recordset without the facilities of MoveNext, RecordCount, Update.
Dim cn As Object
Dim rst As Object
Dim strConnection As String
Set cn = CreateObject("ADODB.Connection")
Set rst = CreateObject("ADODB.Recordset")
' Hard code database location and name
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\\server1\myDatabase.mdb"
' Open the db connection
cn.Open strConnection
' Open the recordset
rst.Open "testQuery", cn
Sheets(1).Range("A2").CopyFromRecordset rst
' Close recordset and db connection
rst.Close
cn.Close

Are you still struggling with this? Try using an ODBC Query. Follow the steps listed here.
http://translate.google.pl/translate?js=n&prev=_t&hl=pl&ie=UTF-8&layout=2&eotf=1&sl=pl&tl=en&u=http%3A%2F%2Fafin.net%2FKsiazkaSQLwExcelu%2FGraficznyEdytorZapytanSqlNaPrzykladzieMsQuery.htm
Make sure you have no nulls in your Union and if you do, you must use the NZ Function to convert nulls into zeros.

Related

How Can I Limit an .OpenDataSource in Word Mail Merge to Only 1 Record Using VBA?

Word 2013, SQL Server 2014
I have a Word Mail Merge template that was originally created for a licensing application that uses an .ini file and an Excel file to generate one certificate from a dataset of many.
I want to make the Word Template work on it's own. I was able to wire up a .udl file to make a connection to the database but it gives me all the tables, then I pick one, then it gives me a document for each row in that table when I only need one.
How can I limit/filter the document that comes from the template to only use a specific table and a specific row (license_id) I request?
Sub AutoNew()
'THIS RETURNS ALL ROWS AFTER I PICK A TABLE
With ThisDocument.MailMerge
.OpenDataSource Name:="C:\Users\or0146575\Desktop\xxx.udl"
.Execute
End With
End Sub
I would wire up the below code to only return 1 row if I knew how.
Dim sql As String
sql = "SELECT full_name, BigLicenseType, LicNosDisplay, expiration_date FROM OpCerts WHERE person_id= 30012"
I decided to take a totally different route and hope it doesn't detract from my points.
Using a datasource file like a .udl or .odc was bringing back all records AND required selecting the table at runtime and I couldn't get the sql to work.
I decided to make the connection with good old ADODB (Microsoft ActiveX Data Objects 2.5 Library in Tools > References) and assign the values to the mailmerge fields or DOCVARIABLE fields or Bookmarks, which ever will work. No sense in using mailmerge for just one record. I hope this helps someone.
Sub AutoNew()
Dim strWhat As String
strWhat = InputBox("Enter License ID", "OpCert Wall Certificate")
Dim conn As ADODB.Connection
Dim cmd As ADODB.Command
Set conn = New ADODB.Connection
conn.ConnectionString = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Initial Catalog=DWS_Licenses;Data Source=WPDHSCLR16C"
conn.Open
Set cmd = New ADODB.Command
cmd.ActiveConnection = conn
cmd.CommandText = "SELECT full_name, BigLicenseType, LicNosDisplay, expiration_date FROM OpCerts WHERE person_id=" & CInt(strWhat)
Dim rs As ADODB.Recordset
Set rs = cmd.Execute() 'Execute stored procedure.
'THIS WILL BE CHANGED TO BOOKMARK(S) PROBABLY IF DOCVARIABLES DOESN'T WORK
ActiveDocument.Variables("expiration_date").Value = rs(3)
'PUT full_name, BigLicenseType, LicNosDisplay TAGS HERE
rs.Close
Set rs = Nothing
Set cmd = Nothing
conn.Close
Set conn = Nothing
End Sub

Using Excel VBA to run SQL query

I am fairly new to SQL and VBA. I have written a SQL query that I would like to be able to call and run from a VBA sub in an excel workbook and then bring the query results into the workbook. I have found some subs online (stackoverflow and other places) that claim to do this but I am having trouble understanding them as they contain no explanation. For example, here is a sub that I found online:
Sub ConnectSqlServer()
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sConnString As String
' Create the connection string.
sConnString = "Provider=SQLOLEDB;Data Source=INSTANCE\SQLEXPRESS;" & _
"Initial Catalog=MyDatabaseName;" & _
"Integrated Security=SSPI;"
' Create the Connection and Recordset objects.
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
' Open the connection and execute.
conn.Open sConnString
Set rs = conn.Execute("SELECT * FROM Table1;")
' Check we have data.
If Not rs.EOF Then
' Transfer result.
Sheets(1).Range("A1").CopyFromRecordset rs
' Close the recordset
rs.Close
Else
MsgBox "Error: No records returned.", vbCritical
End If
' Clean up
If CBool(conn.State And adStateOpen) Then conn.Close
Set conn = Nothing
Set rs = Nothing
End Sub
First of all, would this work? Second, what do I need to replace in the sub (it looks like provider, data source, initial catalog, etc) and where do I find the info to replace them with?
I hope this question is not too confusing and I appreciate your help!
Below is code that I currently use to pull data from a MS SQL Server 2008 into VBA. You need to make sure you have the proper ADODB reference [VBA Editor->Tools->References] and make sure you have Microsoft ActiveX Data Objects 2.8 Library checked, which is the second from the bottom row that is checked (I'm using Excel 2010 on Windows 7; you might have a slightly different ActiveX version, but it will still begin with Microsoft ActiveX):
Sub Module for Connecting to MS SQL with Remote Host & Username/Password
Sub Download_Standard_BOM()
'Initializes variables
Dim cnn As New ADODB.Connection
Dim rst As New ADODB.Recordset
Dim ConnectionString As String
Dim StrQuery As String
'Setup the connection string for accessing MS SQL database
'Make sure to change:
'1: PASSWORD
'2: USERNAME
'3: REMOTE_IP_ADDRESS
'4: DATABASE
ConnectionString = "Provider=SQLOLEDB.1;Password=PASSWORD;Persist Security Info=True;User ID=USERNAME;Data Source=REMOTE_IP_ADDRESS;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Use Encryption for Data=False;Tag with column collation when possible=False;Initial Catalog=DATABASE"
'Opens connection to the database
cnn.Open ConnectionString
'Timeout error in seconds for executing the entire query; this will run for 15 minutes before VBA timesout, but your database might timeout before this value
cnn.CommandTimeout = 900
'This is your actual MS SQL query that you need to run; you should check this query first using a more robust SQL editor (such as HeidiSQL) to ensure your query is valid
StrQuery = "SELECT TOP 10 * FROM tbl_table"
'Performs the actual query
rst.Open StrQuery, cnn
'Dumps all the results from the StrQuery into cell A2 of the first sheet in the active workbook
Sheets(1).Range("A2").CopyFromRecordset rst
End Sub

VBA Connection from Excel 2010 to SQL Server 2008

i'm pretty new to SQL and VBA, so please forgive any audacity the code below may contain. I am working with code written in Excel's VBA. Eventually, the data from the user form in excel will be entered into a SQL database I have created using SQL Server 2008. Right now, I am just trying to open the connection to the SQL database and enter hard coded values into the db. Unfortunately, this has been much more of a challenge than I expected. I have tried playing around with the connection string a few different ways but have had no luck. When the form runs, I get no errors and I can see the data was added into the appropriate excel worksheet (but no changes in the SQL DB). I can see the db on SQL Server Management Studio and add rows from there, but I am unable to add a row to the db via this code. The db is protected solely by windows authentication. Any help would be greatly appreciated.
Sub ConnectSqlServer()
'********SPC DATABASE CONNECTION**********************
'write slurry information to database
'spc_date, mix_type, slurry_lot_num, mixer_num, shift, oper
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sConnString As String
'Create connection string
sConnString = "Provider=sqloledb; Server=SERV; Database=db; Trusted_Connection=True;"
'Create the Connection and Recordset objects
Set oConn = New ADODB.Connection
Set rs = New ADODB.Recordset
'Open connection and execute
conn.Open sConnString
Set rs = conn.Execute("INSERT INTO TBL (col1, col2) VALUES ('val', 'val');")
'Clean up
If CBool(conn.State And adStateOpen) Then conn.Close
Set conn = Nothing
Set rs = Nothing
End Sub
When inserting data, you don't need a recordset object, since an INSERT statement doesn't return a recordset. Instead, try using a command object instead. The command object gives you more control over how your SQL statement is passed to the server, and it also gives you a way to test whether any records were inserted.
I'd also recommend setting Option Explicit at the top of each module, since it will stop you from making mistakes with variables (for instance, you have both conn and oConn in your code, which I don't think you intended.
Here are my edits to your code. It's untested, but I think I've got it right. If it runs without any errors, and recordsAffected returns 1, but there's still nothing on the server, then we'll have to do some more digging.
Sub ConnectSqlServer()
'********SPC DATABASE CONNECTION**********************
'write slurry information to database
'spc_date, mix_type, slurry_lot_num, mixer_num, shift, oper
Dim conn As New ADODB.Connection
Dim cmd As New ADODB.Command
Dim sConnString As String
Dim recordsAffected as Long
'Create connection string
sConnString = "Provider=sqloledb; Server=SERV; Database=db; Trusted_Connection=True;"
'Open connection and execute
conn.Open sConnString
With cmd
.ActiveConnection = conn
.CommandType = adCmdText
.CommandText = "INSERT INTO TBL (col1, col2) VALUES ('val', 'val');"
.Execute recordsAffected 'Includes a return parameter to capture the number of records affected
End With
Debug.Print recordsAffected 'Check whether any records were inserted
'Clean up
If CBool(conn.State And adStateOpen) Then conn.Close
Set cmd = Nothing
Set conn = Nothing
End Sub

How Do I Return Multiple Recordsets from SQL Stored Procedure in Access 2010

I’ve created a pass-through query in Access which executes a stored procedure that searches for a string across all tables in my SQL database. The stored procedure on the SQL server runs as expected, returning multiple Recordsets that contain the value of my search string. However, when I double-click on the pass-through query in Access, in Datasheet View I see the results of only one Recordset. Since it appears that Access is not designed to handle multiple result sets, then how do I use VBA in Access to accomplish this?
exec sqlsp_searchalltables #Tablenames='', #SearchStr='%motion%'
I'm not quite sure how you expected to "bind" your form to the multiple recordsets returned by the stored procedure, but as far as I know the only way to deal with SQL Server stored procedures that return multiple recordsets is to use ADODB.Recordset objects.
(Don't be misled by the "Recordset.NextRecordset Method (DAO)" article here. If you try that approach you will receive run-time error '3847': "ODBCDirect is no longer supported. Rewrite the code to use ADO instead of DAO.")
For example, I have a SQL Server stored procedure that returns two recordsets and I create a pass-through named [dbo_myMultiRsSp_1] to call it:
EXEC dbo.myMultiRsSp #id=1
If I open it in Datasheet View by double-clicking it I see the results of the first recordset.
If I want to process all of the recordsets in VBA I cannot use the pass-through query directly, but I can use its .Connect and .SQL properties as follows
Option Compare Database
Option Explicit
Sub MultiRsSpTest()
Dim cdb As DAO.Database
Dim con As ADODB.Connection, cmd As ADODB.Command
Dim r1 As ADODB.Recordset, r2 As ADODB.Recordset
Set cdb = CurrentDb
Set con = New ADODB.Connection
' connect directly to the SQL Server
' (by using the .Connect property of the pass-through query)
con.Open Mid(cdb.QueryDefs("dbo_myMultiRsSp_1").Connect, 5) ' omit "ODBC:" prefix
Set cmd = New ADODB.Command
cmd.ActiveConnection = con
cmd.CommandType = adCmdText
cmd.CommandText = cdb.QueryDefs("dbo_myMultiRsSp_1").SQL
Set r1 = cmd.Execute
Debug.Print
Debug.Print "First Recordset:"
Do Until r1.EOF
Debug.Print r1(0).Value
r1.MoveNext
Loop
Set r2 = r1.NextRecordset
Debug.Print
Debug.Print "Second Recordset:"
Do Until r2.EOF
Debug.Print r2(0).Value
r2.MoveNext
Loop
' r1.Close (happens implicitly)
Set r1 = Nothing
r2.Close
Set r2 = Nothing
Set cmd = Nothing
Set con = Nothing
Set cdb = Nothing
End Sub

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