I've scoured multiple forums for days now and still stuck. Hoping somebody can shed some light here.
I am increasingly frustrated by SQL syntax differences between MS Office and native SQL, and I've been led to believe that using pass through queries will allow me to use native SQL. I've tried multiple suggestions from various forums to create a pass through query, but I am still faced with Office (syntax) errors in my queries.
Below is a simple example of my code, which Excel/VBA does not like, due to the ISNULL syntax. Please note, it isn't ISNULL itself that is the problem, I know how to work around that. This is just by way of example. The problem is that it should work in native SQL (and it does in SQL Server Management Studio).
For completeness, I am using:
SQL Server 2014
MS Excel 2013
Microsoft DAO 3.6 Object library
I suspect the connection string or the DAO object library may be the culprit, but I've tried multiple others with the same result.
The complete sample (failing on OpenRecordSet) code follows. I would be eternally grateful for any help that can be offered.
Thanks,
Ryan
Option Explicit
Sub TestQuerySQL()
Dim sqlConnect As String, dsnName As String, dbName As String, sqlString As String, db As Database, qd As QueryDef, rs As Recordset
dsnName = "MyDSN"
dbName = "MyDatabaseName"
sqlConnect = "ODBC;DSN=" & dsnName & ";Trusted_Connection=yes;"
sqlString = "Select isnull(d.Name, '???') as DealerName from Dealer d"
Set db = OpenDatabase(dbName, dbDriverNoPrompt, True, sqlConnect)
On Error Resume Next
Set qd = db.CreateQueryDef("", sqlString)
If Err.Number <> 0 Then
MsgBox "CreateQueryDef failed. SQL=>" & sqlString & "< " & Err.Number & " Err=>" & Err.Description & "<", vbCritical
Else
qd.ReturnsRecords = True
Set rs = qd.OpenRecordset(dbOpenSnapshot, dbReadOnly)
If Err.Number <> 0 Then
MsgBox "OpenRecordset Failed. SQL=>" & sqlString & "< Err=>" & Err.Description & "<", vbCritical
Else
MsgBox "Success"
'do someting with the results
End If
End If
End Sub
Specify the dbSQLPassthrough option in the recordset line. Without this designation, the JET/ACE DAO Engine uses its own SQL dialect and hence interprets ISNULL() as the logical function and not SQL Server's ISNULL() as the value function. Below directly opens the recordset without using querydef:
DAO Connection
Set db = OpenDatabase(dbName, dbDriverNoPrompt, True, sqlConnect)
Set rs = db.OpenRecordset(sqlString, dbOpenDynaset, dbSQLPassThrough)
ADO Connection
Alternatively, use an ADO connection where any external SQL engine's dialect can be read:
Dim conn As New ADODB.Connection, rst As New ADODB.Recordset
Dim sqlConnect As String, sqlString As String
' REFERENCE THE MICROSOFT ACTIVEX DATA OBJECTS XX.X LIBRARAY '
sqlConnect = "ODBC;DSN=" & dsnName & ";Trusted_Connection=yes;"
sqlString = "Select isnull(d.Name, '???') as DealerName from Dealer d"
conn.Open sqlConnect
rst.Open sqlString, conn
Related
I am trying to run a stored procedure (using Access VBA) in the SQL database. I can find a few examples of this on SO and on other sites but what I am unable to find is connection string where I can provide my login details as windows authentication won't work because I have to use a different user to connect to SQL database
Here is what I have tried so far (got this from one of the post in SO, unfortunately, I can't find the post anymore):
Sub RunSQLProc()
Dim cdb As DAO.Database, qdf As DAO.QueryDef
Set cdb = CurrentDb
Set qdf = cdb.CreateQueryDef("")
qdf.Connect = "ODBC;" & _
"Driver=SQL Server;" & _
"Server=myServer;" & _
"Database=myDatabase;" & _
"UID=myUsername;" & _
"PWD=myPassword;" & _
"Trusted_Connection=yes;"
' "Driver={SQL Server Native Client 11.0};" & _
' "MARS Connection=True;"
qdf.SQL = "SQLStroedProcedure;"
qdf.ReturnsRecords = False
qdf.Execute dbFailOnError '<-- this line throws the error
Set qdf = Nothing
Set cdb = Nothing
End Sub
On qdf.Execute dbFailOnError line, I get the following error:
Run-time error '3151':
ODBC--connection to 'SQL ServermyServer'failed.
I have managed to connect to the SQL database and run the stored procedure through SQL Server Management Studio. This tells me that the details in my connection string are correct (although I haven't provided the actual details here) but I suspect there is an issue with my connection string.
Happy to provide more details. Any help is much appreciated, thanks
P.S. Apologies for my lack of knowledge in Access. I just don't use it very often
to use SQL Server security remove Trusted_Connection=yes.
https://www.connectionstrings.com/microsoft-sql-server-odbc-driver/
I'm trying to connect to an SQL Server through Automatisation in VBA, so each time Excel starts, the SQL Statement updates the table in Excel.
Problem is, that my Connection string always throws an exception:
"Run-time error "-2147217843 (80040e4d)'; Automation error"
I have following data provided:
Servername, though it has a comma in it's Name
Database Name
I also have a Windows user (accountname & Password), that I run the Statements from, but I doubt, it is necessary to provide this information to the SQL Server.
the Connection string Looks as follows:
Public Sub OpenConnection2(pServer As String, pCatalog As String)
Dim mDataBase As New ADODB.Connection
Dim mRS As New ADODB.Recordset
Dim mCmd As New ADODB.Command
Call mDataBase.Open("Provider=SQLOLEDB;Initial Catalog=" & pCatalog & ";Data Source=" & pServer & ";")
mCmd.ActiveConnection = mDataBase
End Sub
As you, FunnyO, already stated, it is the connection string.
You probably do not have the correct driver definition.
Try something like that:
strCnn = "Provider=SQLNCLI11;Server=" & pServer & ";Database=" & pCatalog & ";Integrated Security=SSPI;"
Sure, a very simple connection string. But it should work out as it is a very common one...
I've researched and researched... yet to find a solution. I've read people having similar trouble because of the encoding, but I've tried retyping the query and even used convert to UTF-8 inside Notepad++. Any ideas?
Error:
Incorrect syntax near 'NEW'.
Query:
delete from [orgDefaults]
where ([orgcode] = N'NEW')
and ([ctlName] = N'AllowReportables')
This is being executed inside a VB.NET program I've created using this OLEDB driver:
Dim conn As New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & updates_mdb & ";Jet OLEDB:Database Password=" & Settings.Password & ";")
You are using the wrong driver to connect to SQL server.
You are using the MS Access Jet Engine. But this uses another SQL syntax, that's why it does not work.
Just use the SQL Server OLEDB driver, and it will work.
Just for hack of it , try this:
Using conn As New SqlConnection("sqlServer Conn string - connectionstrings.com")
Dim sql As String = "DELETE FROM [orgDefaults] WHERE [orgcode] = #1 AND [ctlName] = #2"
Using cmd As New SqlCommand(sql, conn)
cmd.Parameters.AddWithValue("#1", "NEW")
cmd.Parameters.AddWithValue("#2", "AllowReportables")
cmd.CommandType = CommandType.Text
conn.Open()
Dim retVal As Integer = cmd.ExecuteNonQuery()
If retVal > 0 Then
Debug.WriteLine("Success - deleted 1 or more records")
' remeber, retVal may not match num of rows deleted.
' Rather, it indicates total rows affected
Else
Debug.WriteLine("Still Success - deleted Nothing")
End If
End Using
End Using
This should give you better picture [if you have some "special" issue] because Sql executed this way will match your values and field types better.
I am doing an excel macro in order to automate some query what eventually I run in SQL Server. My problem is that I don't know how the server could alert excel if a query did not succeed.
For example, I am importing a file, and there is no syntax error, but it might result in error if bulk insert statement is not set properly. For the SQL connection I use the following:
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sConnString As String
' Create the connection string.
sConnString = "Provider=SQLOLEDB;Data Source=localhost;" & _
"Initial Catalog=" & MyDatabase & ";" & _
"Integrated Security=SSPI;"
' Create the Connection and Recordset objects.
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
conn.Open sConnString
Set rs = conn.Execute(Myquery)
If I have a syntax error while compiling the code it stops which is good. But if I have another problem, e. g. the database name is not good, the table already exists, then the program runs with no error, I only can detect when I check it in SQL Server. I really want to know somehow whether the query run has resulted in error and then code some alerting message then into my macro. How can I do that?
Every help is much appreciated!
The ADO connection object has an Errors collection, which you can check after running your SQL:
conn.Errors.Clear
Set rs = conn.Execute(Myquery)
If conn.Errors.Count > 0 Then
For i = 0 To conn.Errors.Count
Debug.Print conn.Error(i).Number
Debug.Print conn.Error(i).Source
Debug.Print conn.Error(i).Description
next i
End If
That should get you started. You may find that you're seeing an 'error zero' that's actually a status message; if so, you'll have some additional coding to to do.
I found this helpful but needed to use:
Debug.Print conn.Errors.Item(i).Description
Debug.Print conn.Errors.Item(i).Source
Debug.Print conn.Errors.Item(i).NativeError
I might be using a different connection type
I have used Ben Clothier's suggestion from his Office Blog Power Tip (http://blogs.office.com/2011/04/08/power-tip-improve-the-security-of-database-connections/) to create a DSN-less connection with cached credentials so that users' UID and PWD aren't saved, or required multiple times, when working in the Access interface. Have others done this? If so, what has been your approach when you need to use an ADO connection instead of DOA to reach SQL from Access via VBA? How do you open a adodb connection without having to provide the User ID and Password again, or having to put it in the code?
(I'm using Access 2013 frontend, SQL 2008 R2 backend, SQL Server Security)
Thanks in advance!
My Cached Connection code works like this:
Public Function InitConnect(strUserName As String, strPassword As String) As Boolean
' Description: Is called in the application’s startup
' to ensure that Access has a cached connection
' for all other ODBC objects’ use.
Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim rst As DAO.Recordset
Dim strConnection As String
strConnection = "ODBC;DRIVER=sql server;" & _
"SERVER=******;" & _
"APP=Microsoft Office 2010;" & _
"DATABASE=******;" & _
"Network=DBMSSOCN;"
Set dbs = DBEngine(0)(0)
Set qdf = dbs.CreateQueryDef("")
With qdf
.Connect = strConnection & _
"UID=" & strUserName & ";" & _
"PWD=" & strPassword & ";"
.SQL = "Select Current_User;"
Set rst = qdf.OpenRecordset(dbOpenSnapshot, dbSQLPassThrough)
End With
InitConnect = True
ExitProcedure:
On Error Resume Next
Set rst = Nothing
Set qdf = Nothing
Set dbs = Nothing
Exit Function
End Function
Then when I need to access data I can do this (Note the UID and PWD are not required):
Dim dbs As DAO.Database
Set dbs = OpenDatabase("", False, False, "ODBC;DRIVER=sql server;SERVER=*****;APP=Microsoft Office 2010;DATABASE=*****;Network=DBMSSOCN")
I can also set the ODBC connection to pass-through queries as well in Access or VBA. But these connections work only when the connection string is IDENTICAL to what was originally used in my Cached Connection code. So, when I need an ADODB connection (as it seems sometimes ADO is needed?), the string obviously isn't going to be identical.
For Example:
Dim cn As New ADODB.Connection
cn.Open "Provider = sqloledb;Data Source=*same as "SERVER"*;Initial Catalog=*same as "DATABASE"*;User Id=****;Password=****"
This type of connection only works if I supply a User Id and Password. How can I write it so that I don't need them? ~Thanks!
Although ACCESS has some weak points regarding security, you can do few things to minimize the risks. One of them would be compile the DB to ACCDE. This way VBA is compiled and not visible.
You can create a public function that returns a string
Public Function GET_CONNECTION_STRING() as STRING
' construct your connection string here with server name and password
GET_CONNECTION_STRING = "DRIVER={" & Driver & "};PORT=" & mPort & ";DATABASE=" & mDatabase & ";SERVER={" & mServer & "};UID=" & mUser & ";PWD={" & mPassword & "};"
End Function
then create an AutoExe macro that runs when the application is opened.
in your AutoExe perform refreshing links to your linked tables. something similar to what you have.
For Each tdf In db.TableDefs
If tdf.connect <> vbNullString Then
tdf.connect = GET_CONNECTION_STRING & ";TABLE=" & tdf.name
tdf.RefreshLink
End If
Next tdf
you can do the same for existing pass through queries:
For Each myQuerydef In MyDB.QueryDefs
If Left(myQuerydef.connect, 4) = "ODBC" Then
myQuerydef.connect = "ODBC;" & GET_CONNECTION_STRING
myQuerydef.Close
End If
Next
in addition you can have some other public functions to get current logged in username.
something like
public function getCrruserID() as int
'check your public variable crr_user_id if its empty redirect to login
if nz(crr_user_id,0) = 0 then
'go to login and save the user id after successful login
else
getCrruserID = crr_user_id
end if
end function
use simple DAO to execute sql code like
dim db as DAO.Database
set db = currentdb
dim rs as Dao.Recordset
set rs = db.openrecordset("select something from your linked table")
or
db.execute "update command", dbfailonerror
to your last question. if you save something in memory it will be destroyed once your application is closed.
EDIT:
if you have more than 50 linked tables it might be not a good idea to refresh them at every startup. Instead you can create a Local table containing your [local_Appversion, isFreshInstall] and some other variables as per your need. Every time your user receives an update the freshInstall will be true and code your App to connect and refresh all tables. (just to make sure client will get uninterrupted connection)
so in your autoExe code: if its freshInstall then
connect and refreshlinks if not just set the connectionString. (usually a splash screen after login to perform this action)
After successful connection just update the local isFreshInstall value to false for a quicker start next time.
you can also have a dedicated menu where user can click and refresh links manually.(in case if the connection get dropped)
something like
if your organisation has a domain you can allow trusted connection using windows login name
good luck.