DSN-less Connection MS Access front end and SQL server backend - sql-server

I don't normally program in MS Access VBA so forgive my question if it's stupid.
So I'm using MS Access 2010 as a front end and SQL Server 2014 as a backend. (I don't have a choice in frontend interface so please no suggestions on alternate options).
I'd like to programatically link SQL server's backend to my MS Access frontend. I read here at DJ Steele's DSN-less connection page that I can use the code he provided here to make a DSN-less connection to SQL server as a backend.
So I copied that into a VBA Access module and opened another module and ran this code to run the DJ Steele code in an attempt to connect to a small SQL Server database I made:
Option Compare Database
Sub runThis()
FixConnections "AServerNameHere", "MS_Access_BackEnd_Test"
End Sub
As far as I can tell from the VBA debugger it gets to
Set dbCurrent = DBEngine.Workspaces(0).Databases(0)
then that value seems to be empty. I'm not sure how else to proceed with this since as far as I was able to find this is one of the few full examples of a DSN-less connection I could find.
I'd like to not use the DSN method of linking a SQL server to a database since that would require me to go visit people and their computers in order to make the links. (And who'd want to to that? LOL)
I've also looked at similar questions that were linked to me while writing this question and this was close to what I wanted, but it kept giving me "Compile error: Constant expression required" for input of:
LinkTable "MS_Access_BackEnd_Test", "Table_1"
and
LinkTable "MS_Access_BackEnd_Test", "Table_1", , "AServerNameHere"
Again I'm not familiar with MS Access VBA so forgive the question if it's lame.

Looking at DJ Steele's code, I got it working apart from the line
' Unfortunately, I'm current unable to test this code,
' but I've been told trying this line of code is failing for most people...
' If it doesn't work for you, just leave it out.
tdfCurrent.Attributes = typNewTables(intLoop).Attributes
which I had to comment out.
Using Set dbCurrent = CurrentDb() does essentially the same thing as Set dbCurrent = DBEngine.Workspaces(0).Databases(0) but the latter is meant to be a lot faster ... which these days means it takes 10 microseconds instead of 100 :-o
You still need dbCurrent as a reference to the current Access front end which is where the linked table objects live, even if the data is coming from elsewhere.
Edit: working for me
I added a debug.print line to monitor what's going on
' Build a list of all of the connected TableDefs and
' the tables to which they're connected.
For Each tdfCurrent In dbCurrent.TableDefs
Debug.Print tdfCurrent.Name, tdfCurrent.Connect
If Len(tdfCurrent.Connect) > 0 Then
If UCase$(Left$(tdfCurrent.Connect, 5)) = "ODBC;" Then
...
and then
? currentdb().TableDefs("dbo_Person").Connect
ODBC;DSN=TacsData;APP=Microsoft Office 2003;WSID=TESTXP;DATABASE=TacsData;Trusted_Connection=Yes;QuotedId=No
FixConnections "TESTXP\SQLEXPRESS", "TacsData"
MSysAccessObjects
MSysAccessXML
...
MSysRelationships
Table1
dbo_Person ODBC;DSN=TacsData;APP=Microsoft Office 2003;WSID=TESTXP;DATABASE=TacsData;Trusted_Connection=Yes;QuotedId=No
? currentdb().TableDefs("dbo_Person").Connect
ODBC;DRIVER=sql server;SERVER=TESTXP\SQLEXPRESS;APP=Microsoft Office 2003;WSID=TESTXP;DATABASE=TacsData;Trusted_Connection=Yes

Related

Query data from SQL to MS Access: Local Tables vs Pass-Through Tables

I've created an application that uses the following logic to query data from SQL to my MS Access App.
Using an ODBC connection I execute a stored procedure
Using This is assigned as a Pass-Through Query to pull the data locally.
It looks something like this:
strSQL = "EXEC StoredProcedure " & Variable & "
Call ChangeQueryDef("qryPassThrough", strSQL)
Call SQLPassThrough(strQDFName:="qryPassThrough", _
strSQL:=strSQL, strConnect:=gODBCConn)
Me.frmDataSheet.Form.RecordSource = "qryPassThrough"
But, recently we have upgraded our SQL Server to 2016 using a high availability failover system - hence our connection string has changed to connect to a listener like so:
gODBCConn = "ODBC;Driver= {SQL Server Native Client 11.0};Trusted_Connection=Yes;Regional=Yes;Database=" & varDB & ";MultiSubnetFailover=Yes;IntegratedSecurity=SSPI;Server=tcp:SERVER_LISTENER,1433;"
However, it looks like using SQL Server Native Client in the connection string is not the same as what we originally had which was SQL Server. Certain data types have changed and do not work in Access.
Is there a better way for me to query data from SQL and persist/display this data in access using ADO or an alternative method?
EDIT Based on Comment:
The issue I'm having is that I have tables in SQL using the data type: Decimal(12,2). With some testing and experimenting this seems to fail when using an ODBC pass-through query. But changing the data type to Float seems to work fine. Then there are other datatypes which seem to error too which I've not managed to find yet. It just seems there are a few difference which I'm not aware of and I'm keen to find a better way to load data into my Access App.
EDIT 2
This is the error message I get relating to the data type issue.
Sounds like you're not really interested in making the underlying data structure compatible with Access, so:
How to load an ADODB recordset into a datasheet form
Create the form
First, create a datasheet form. For this example, we're going to name our form frmDynDS. Populate the form with 256 text boxes, named Text0 to Text255. To populate the form with the text boxes, you can use the following helper function while the form is in design view:
Public Sub DynDsPopulateControls()
Dim i As Long
Dim myCtl As Control
For i = 0 To 255
Set myCtl = Application.CreateControl("frmDynDS", acTextBox, acDetail)
myCtl.NAME = "Text" & i
Next i
End Sub
VBA to bind a recordset to the form
First, we're going to allow the form to persist, by allowing it to reference itself:
(all on in the code module for frmDynDS)
Public Myself As Object
Then, we're going to add VBA to make it load a recordset. I'm using Object instead of ADODB.Recordset to allow it to both take DAO and ADODB recordsets.
Public Sub LoadRS(myRS As Object)
Dim i As Long
Dim myTextbox As textbox
Dim fld As Object
i = 0
With myRS
For Each fld In myRS.Fields
Set myTextbox = Me.Controls("Text" & i)
myTextbox.Properties("DatasheetCaption").Value = fld.NAME
myTextbox.ControlSource = fld.NAME
myTextbox.ColumnHidden = False
i = i + 1
Next fld
End With
For i = i To 255
Set myTextbox = Me.Controls("Text" & i)
myTextbox.ColumnHidden = True
Next i
Set Me.Recordset = myRS
End Sub
Use the form
(all in the module of the form using frmDynDS)
As an independent datasheet form
Dim frmDS As New Form_frmDynDS
frmDS.Caption = "My ADO Recordset"
frmDS.LoadRS MyAdoRS 'Where MyAdoRS is an open ADODB recordset
Set frmDS.Myself = frmDS
frmDS.Visible = True
frmDS.SetFocus
Note that you're allowed to have multiple instances of this form open, each bound to different recordsets.
As a subform (leave the subform control unbound)
Me.MySubformControl.SourceObject = "frmDynDS"
Me.MySubformControl.Form.LoadRS MyAdoRS 'Where MyAdoRS is an open ADODB recordset
Warning: Access uses the command text when sorting and filtering the datasheet form. If it contains a syntax error for Access (because it's T-SQL), you will get an error when trying to sort/filter. However, when the syntax is valid, but the SQL can't be executed (for example, because you're using parameters, which are no longer available), then Access will hard crash, losing any unsaved changes and possibly corrupting your database. Even if you disable sorting/filtering, you can still trigger the hard crash when attempting to sort. You can use comments in your SQL to invalidate the syntax, avoiding these crashes.
You previously used the pretty ancient, original ODBC Driver for SQL Server simply named SQL Server. You made the right decision to use a newer driver to support your failover cluster. But I would not recommend to use SQL Server Native Client. Microsoft says, It is not recommended to use this driver for new development.
Instead I would use the Microsoft ODBC Driver 13.1 for SQL Server. This is the most recent and recommended (by Microsoft) ODBC Driver for SQL Server.
Your main issue seems to be a translation issue between Access and SQL Server via the ODBC layer. So, using the more modern driver might very well make this problem go away. - I do not know if it solves your problem, but this is the very first thing I would try.

How to dynamically change server url withing http connection manager SSIS?

I am fairly new to SSIS, Visual studio. Thought that might be good to mention in the beginning.
What I wanted to achieve was to download a certain xls file from http://www.ads-slo.org/statistika/ website and store it in a certain folder on my computer. I have achieved that, but the problem is that I know how to do it one file at a time. I did it by opening new connection, going to http connection and in the manager typing the server url: which in my case if lets say we start with January 2016 was this:http://www.ads-slo.org/media/xls/2016/Januar-2016.xls. After doing so I've constructed a script task or more or less copied it from a website that downloads the file given a certain url based on the connection manager.
My problem is that I would like to download all of the files on this site, so starting with January 2007 and ending with January 2016 with a single package and by not changing my connection manager server url settings 100 times.
Is there any way you might help me. I would be forever grateful.
Thank you in advance.
Kind regards, Domen
Here is one very simple example (it can be improved - see comments after code block) of changing a connection string dynamically by using a Script Task. You can also dynamically change connection strings using expressions and the Connection Manager's expressions property. However, since you are using a Script Task to handle the downloads, I have demonstrated it using one.
As you haven't tagged the Script language (VB or C#) you are using, I have written a rough draft in VB.
I have added comments, but stackoverflow syntax highlighting interprets it strangely; apologies.
Public Sub Main()
' Get the HTTP Connection
Dim connObj As Object = Dts.Connections("HTTP Connection Manager").AcquireConnection(Nothing)
Dim connection As New HttpClientConnection(connObj)
' Static list of month names for dynamic connection string (obviously add as many as needed)
Dim monthNames As String() = New String() {"Januar", "February", "March"}
' Nested loop - for each year and month, try to download the Excel file
For Y As Integer = 2007 To 2016 Step 1
For M As Integer = 0 To monthNames.Length - 1 Step 1
' Set the assumed name of the remote file
Dim remoteFileName As String = monthNames(M) + "-" + Y.ToString() + ".xls"
' Change the connection string a.k.a dynamic connection string
connection.ServerURL = "http://www.ads-slo.org/media/xls/" + Y.ToString() + "/" + remoteFileName
' Set where to download the file to
Dim localFileName As String = "C:\Temp\" + remoteFileName
Try
connection.DownloadFile(localFileName, True)
Dim buffer As Byte() = connection.DownloadData()
Dim data As String = Encoding.ASCII.GetString(buffer)
Catch E As Exception
' File may not exist on remote server, delete the blank copy it attempted to create
File.Delete(localFileName)
End Try
Next
Next
Dts.TaskResult = DTSExecResult.Success
End Sub
How can this be improved?
One potential improvement is to parse the remote server for the folders and directory contents (to save having static lists of month names, hardcoded start and end years and building file names) using a HttpWebRequest.
However, there might be an issue with the remote server permissions in allowing such requests to be made so you would have to investigate further and speak with the server administrator.
Testing the above code, it successfully downloaded the Januar-2015 and Januar-2016 Excel files from the website.

SSIS Package works in IDE but does not after deployment

So I have this SSIS package that works flawlessly when I start it within Visual Studio Integration 2012. However when I deploy this package via the SQL Catalog it neither works or breaks. It just runs with no output.
I have no idea why this is. My package is simple.
Query a DB table
Place data into a Recordset Destination using a local variable
Pass the local variable along with some project parameters into a script that is embedded into a sequence > foreach container. The script then updates the same db table with a new value based on the logic within the script and it also sends out an email using the data pulled from the query.
I know that my query in #1 is working. I can see it through my job execution reports... it writes in all rows from the DB table. Then it just sits in my script doing God knows what... So I am guessing since it is not my query it must be one of the project params/connections (that I set using an environment). Here is my syntax for these. Do you see anything wrong with them? Please ignore the code I left out as I am sure it is not breaking there. Also I am 100% positive I am setting the values correctly in my environment and that they are properly assigned in the package.
Here is my relevant code snippets (do not mind order):
Dim oleDA As New OleDbDataAdapter
oleDA.Fill(dt, Dts.Variables("User::CustomerRows").Value)
ScriptMain.Wrapper = New ET_AutoRenewal.WrapperClass(Dts.Variables("$Project::etUserName").Value.ToString(), Dts.Variables("$Project::etPassword").Value.ToString())
Dim cm As ConnectionManager = Dts.Connections("test")
Dim sqlConn As SqlClient.SqlConnection
Dim sqlCmdText As String
sqlConn = DirectCast(Dts.Connections("test").AcquireConnection(Dts.Transaction), SqlClient.SqlConnection)
sqlCmdText = "UPDATE [" + Dts.Variables("$Project::sDatabaseName").Value.ToString() + "].[dbo].[temp_AutoRenewal_listpull] SET [process_flag] = 1 WHERE [confirmation_num] = '" + xmlDict.Item("confirmation_num") + "'"
So if that all looks good, which to me it does. How could this happen, it works fine in VS but not when deployed. What is more frustrating is nothing fails, it simply runs and there is no output (ie no table updates or emails sent out).
Are there any techniques to debug a deployed package?

Unable to query nVarChar(Max) field in Access 2010

have used Stack Overflow as a resource hundreds of times, but my first time posting a question for some help!
I've got a table in SQL Server 2005 which contains 4 nVarChar(Max) fields.
I'm trying to pull out the data from an Access (2010) VBA Module using ADO 2.8
I'm connecting using SQL driver SQLNCLI10
(I can't use a linked table, as the 'table' I will ultimately be querying is a Table-Valued Function)
When I then print / use the recordset, the data is getting jumbled and concatenated with other fields in the same record - with a bunch of obscure characters thrown in.
The VBA: (various other methods were tried with the same result)
Sub TestWithoutCasting()
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim i As Integer
cn.Open "Data Source=ART;DataTypeCompatibility=80;MARS Connection=True;"
Set rs = cn.Execute("SELECT * FROM JobDetail WHERE JobID = 2558 ORDER BY SeqNo ASC")
Do While Not rs.EOF
For i = 1 To rs.Fields.Count
Debug.Print rs.Fields(i).Name & ": " & rs.Fields(i).Value
Next i
rs.MoveNext
Loop
End Sub
Example Output:
SeqNo: 1
CommandID: 2
Parameter1: 2 Daily Report é [& some other chars not showing on here]
Parameter2: [Null]
Parameter3: [Null]
Parameter4: [Null]
Description: Daily Report
Active: False
Expected Output:
SeqNo: 1
CommandID: 2
Parameter1: SELECT Day_Number ,Day_Text ,Channel_Group_ID [...etc]
Parameter2: [Null]
Parameter3: [Null]
Parameter4: [Null]
Description: Daily Report
Active: False
So, it's grabbing bits of data from other fields instead of the correct data (in this case, it's an SQL statement)
I then tried casting the nvarchar(max) fields as text at source
View Created:
CREATE VIEW TestWithCast
AS
SELECT jd.JobID, jd.SeqNo, jd.CommandID
,cast(jd.Parameter1 as text) as Parameter1
,cast(jd.Parameter2 as text) as Parameter2
,cast(jd.Parameter3 as text) as Parameter3
,cast(jd.Parameter4 as text) as Parameter4
,jd.[Description]
,jd.Active
FROM JobDetail jd
Now, I initially had some luck here - using the same code as above does bring back data - but when I use this code in my main code (which jumps in & out of other procedures); as soon as I've queried the first result of the recordset, it appears to wipe the rest of the records / fields, setting them to Null. I also tried setting the value of each field to a variable whilst the rest of the vba runs before getting the next record - but this doesn't help either.
It almost feels like I need to dump the recordset into a local Access table, and query from there - which is a bazaar workaround for what is already a workaround (by casting as text).
I there something I'm completely missing here, or do I indeed need to cast as text and load to a local table?
Thanks for any help - it's driving me mad!
ps. Hope I've given the right level of detail / info - please let me know if I missed anything key.
EDIT:
Yikes, I think I've done it / found the issue...
I changed the driver to SQLSRV32 (v6.01) - and seems to work fine directly against the text casted field.
So... why would it work with an older driver but not the newer 'recommended' (by various sources I read) as the one to use.
And... will there be a significant drawback in using this over the native client?
EDIT 2:
Ok, I've tried a few drivers on a few machines, in each case with both the TEXT CASTING and Directly to VARCHAR MAX..
[On my windows 7 machine w/ SQLSMS 2008]
SQL Native Client 10.0 - Neither method works reliably with this driver
SQL Server 6.01 - BOTH methods appear to work reliably - further testing needed though
[On our production server w/ SQLS 2005]
SQL Native Client (v2005.90) - Does not work at all with varchar(max), but DOES work with text casting
SQL Server (v2008.86) - BOTH methods appear to work reliably - further testing needed though
This should make deployment interesting!
It's not a real answer, because I did not test it, but ... You are using a "DataTypeCompatibility=80" parameter in your connection. As far as I know, DataTypeCompatibility=80 refers to SQL Server 2000, where the nvarchar(max) field type was still not implemented.
I had the same problem, solved it by converting the field to an nvarchar(1000). Would be an easy, compatible solution for your problem if 1000 chars is enough.

An Erroneous SQL Query makes browser hang until script timeout exceeded

I have an admin page in a Classic ASP web application that allows the admin user to run queries against the database (SQL Server 2000)
Whats really strange is that if the query you send has an error in it (an invalid table join, a column you've forgotten to group by etc) the BROWSER hangs (CPU usage goes to maximum) until the SERVER script timeout is exceeded and then spits out a timeout exceeded error (server and browser are on different machines, so not sure how this happens!) I have tried this in IE 8 and FF 3 with the same result.
If you run that same query (with errors) directly from SQL Enterprise Manager, it returns the real error immediately. Is this a security feature? Does anyone know how to turn it off? It even happens when the connection to the database is using 'sa' credentials so I dont think its a security setting :(
Dim oRS
Set oRS = Server.CreateObject("ADODB.Recordset")
oRS.ActiveConnection = sConnectionString
// run the query - this is for the admin only so doesnt check for sql safe commands etc.
oRS.Open Request.Form("txtSQL")
If Not oRS.EOF Then
// list the field names from the recordset
For i = 0 to oRS.Fields.Count - 1
Response.Write oRS.Fields(i).name & " "
Next
// show the data for each record in the recordset
While Not oRS.EOF
For i = 0 to oRS.Fields.Count - 1
Response.Write oRS.Fields(i).value & " "
Next
Response.Write "<br />"
oRS.Movenext()
Wend
End If
Try taking the core contents of the ASP (the bit that does the work) and stick it into a pure VBS script and run that from a cmd prompt via cscript (i.e., cscript bit_wot_does_stuff.vbs) and see how quickly that comes back. [Change Server.CreateObject to CreateObject and any Response.Write to WScript.Echo for it to work in a cmd environment].
Hopefully this will tell you whether the problem lies either in ASP/IIS or within some oddity with the ADO libs (although the code looks okay, but I haven't executed it :-) ).
Hopefully it'll give you something to then trace a bit further.
SOLUTION (Just to close the post coz it aint gonna get solved!)
Upgrade to SQL Server 2005/2008/R2 - solved the problem :)

Resources