Access Excel workbook through ADODB - Mutiple users at same time - database

I am trying to access Excel workbook placed in a share path through ADODB connection in different remote machines. But when multiple remote machines trying to access the same Excel sheet, it thows the following error:
The Microsoft Office Access database engine cannot open or write to the file ''. It is already opened exclusively by another user, or you need permission to view and write its data.
Connection String used:
strConnection_Write = "Provider=Microsoft.jet.OLEDB.4.0;Data Source=" & _
Runmanager_path & ";Mode= Share Deny None" & _
";Extended Properties=""Excel 8.0;HDR=Yes;"";"
Please suggest me a solution.

I ran into the same issue a while back with OLEDB, got past it by using DAO...
Dim DBworkbook As DAO.Database
Dim DBSheet As DAO.Recordset
Set DBworkbook = OpenDatabase("C:\File.xls", False, False, "Excel 8.0;HDR=Yes;")
Set DBSheet = DBworkbook.OpenRecordset("SELECT * FROM [Sheet1$]")
DBSheet.MoveFirst
Do Until DBSheet.EOF
If DBSheet!Name = "Bob" Then
DBSheet.Edit
DBSheet!Addeess = "5 Something Street"
DBSheet.Update
End If
DBSheet.MoveNext
Loop

Related

How to connect/link SQL db to Access database using SSIS?

I have a SQL database that centralizes all data within our agency; however, we also receive data from outside sources that needs to be parsed into our db structure. One of our partners maintains an Access 2016 database that they provide to us weekly to perform various analyses. My goal is to build an SSIS (2017) package that will move data from the Access db to SQL tables; replacing the Access db w/ an updated version and re-running the SSIS package on a scheduled basis.
I am trying to set up a connection manager for the Access database, but I cannot find a driver option in the provided DSN list for .accdb files, only .mdb files types, when I try to build the connection string. How do I create the connection for an Access 2016 database (i.e., .accdb)???
You don't need SSIS for something like this. You can simply export data from MS Access to SQL Server, without replying on another app.
This VBA sample will export data from a table in Access to SQL Server.
Option Compare Database
Private Sub Command0_Click()
Dim sTblNm As String
Dim sTypExprt As String
Dim sCnxnStr As String, vStTime As Variant
Dim db As Database, tbldef As DAO.TableDef
On Error GoTo ExportTbls_Error
sTypExprt = "ODBC Database" 'Export Type
sCnxnStr = "ODBC;DRIVER=SQL Server;SERVER=your_sever_name" & stServer & ";DATABASE=your_database_name" & stDatabase & ";Trusted_Connection=Yes"
vStTime = Timer
Application.Echo False, "Visual Basic code is executing."
Set db = CurrentDb()
'need a reference to Microsoft DAO 3.x library
For Each tbldef In db.TableDefs
'Don’t export System and temporary tables
Debug.Print tbldef.Name
If tbldef.Name Like "*Table1*" Then
sTblNm = tbldef.Name
DoCmd.TransferDatabase acExport, sTypExprt, sCnxnStr, acTable, sTblNm, sTblNm
End If
Next tbldef
MsgBox "Done! Time taken=" & (Timer - vStTime)
On Error GoTo 0
SmoothExit_ExportTbls:
Set db = Nothing
Application.Echo True
Exit Sub
ExportTbls_Error:
MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure ExportTblsODST"
Resume SmoothExit_ExportTbls
End Sub
See the link below for more info.
https://www.mssqltips.com/sqlservertip/2484/import-data-from-microsoft-access-to-sql-server/

Excel Visual Basic ADODB SQL connection string not working

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...

Keeping UID and PWD out of an ADO connection string in an ODBC DSN-less Database and a DAO cached connection?

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.

Programmatically creating linked tables in access

We need to find a way to programatically ****link all the tables in a SQL Server database to an access db. We will be invoking this access database from a program that uses .net/SQL Server 2008.
While invoking the application we would like to add the linked tables so that the users can just run the reports/modules from access without having to worry about linking the tables. Is there a way we can do this?
Here are some notes.
Dim sLocalName As String
Dim tdf As TableDef
Dim rs As dao.Recordset
''This is a basic connection string, you may need to consider password and so forth
cn = "ODBC;DSN=TheDSNName;Trusted_Connection=Yes;APP=Microsoft Office 2010;DATABASE=TheDatabaseName;"
''All fields from tables
strSQL = "SELECT TABLE_CATALOG, " _
& "TABLE_SCHEMA, " _
& "TABLE_NAME, " _
& "TABLE_TYPE " _
& "FROM [" & cn & "].INFORMATION_SCHEMA.tables " _
& "WHERE TABLE_TYPE = 'BASE TABLE'"
Set rs = CurrentDb.OpenRecordset(strSQL)
Do While Not rs.EOF
sLocalName = rs!TABLE_SCHEMA & "_" & rs!TABLE_NAME
With CurrentDb
If DLookup("Name", "MSysObjects", "Name='" & sLocalName & "'") <> vbNullString Then
If .TableDefs(sLocalName).Connect <> cn Then
.TableDefs(sLocalName).Connect = cn
.TableDefs(sLocalName).RefreshLink
End If
Else
''If the table does not have a unique index, you will neded to create one
''if you wish to update.
Set tdf = .CreateTableDef(sLocalName)
tdf.Connect = cn
tdf.SourceTableName = rs!TABLE_NAME
.TableDefs.Append tdf
.TableDefs.Refresh
''This will produce a message box if the table does not have a unique index
''DoCmd.TransferDatabase acLink, "ODBC Database", cn, acTable, rs!TABLE_NAME, sLocalName
End If
End With
rs.MoveNext
Loop
You'll need an ODBC connection to the SQL database. Once this connection ready, you can use it for all tables that you want to link:
DoCmd.TransferDatabase acLink, _
"ODBC Database", _
myODBCconnection, _
myDatabaseName, _
acTable, _
myTableName
I guess you can declare your ODBC connector "on the fly", as proposed here for example.
To enumerate your tables, you have the following options:
Enumerate them in the code: one transferDatabase line per table
Save the table names in a local table, and browse the table
Save the table names in a file (text, xml) anywhere on the network and browse the file
Access the system table on the server that holds the table list, and browse the table
Use the ADOX object to browse all tables in your database server: be carefull not to include system tables. This solution might be also quite confusing because you'll have to first open an ADODB connection to your database, and you'll then use an ODBC connection to open the tables
In all cases, this procedure shall be launched with the autoexec macro, meaning that links will be created\updated each time the user opens the mdb client.
You would use ADOX to do the actual linking.
As far as enumerating the tables in a database you are connected to, you could do something as simple as running this query against your SQL Server, but there are a lot of ways to skin that cat:
SELECT * FROM INFORMATION_SCHEMA.TABLES
You can achieve the equivalent by using a Linked Server in SQL Server that points to the Access db. This will give you access to all the tables in the Access db so that you can reference them like:
Select ..
From [LinkedServerName]...[AccessTableName]
Btw, a linked server may be overkill for what you want. Look into the OPENROWSET function which effectively let's you pass a connection string.
EDIT: I originally read the question to literally mean "link tables in SQL Server to access" which I translated to mean from SQL to Access. So, given that, my solution would apply. However, if the desire is to go from Access to SQL, then that is different and other solutions presented would be more appropriate.

What is the fastest method for importing from the active sheet in Excel to SQL Server?

What is the fastest way to import data into a SQL Server database directly from the active worksheet in Excel?
I have had great success simply looping through the rows and columns, generating a SQL string, opening an ADODB.Connection and executing the SQL. That solution, however, is too slow for large datasets.
So I am testing the Jet Provider with ADO. It's definitely faster, but it requires that the workbook be saved, and I cannot require the user to save the workbook before uploading from it.
The following code works if the workbook has been saved, but if ActiveWorkbook has never been saved ActiveWorkbook.Path returns a null string and the code fails.
Sub UploadViaJet()
Dim objADO As New ADODB.Connection
Dim strSQL As String
Dim lngRecsAff As Long
Set objADO = New ADODB.Connection
objADO.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & ActiveWorkbook.Path & _
"\" & ActiveWorkbook.Name & ";" & _
"Extended Properties=Excel 8.0"
strSQL = "SELECT * INTO [odbc;Driver={SQL Server};" & _
"Server=<server>;Database=<database>;" & _
"UID=<UID>;PWD=<PWD>].test_table " & _
"FROM [" & ActiveSheet.Name & "$]"
objADO.Execute strSQL, lngRecsAff, adExecuteNoRecords
End Sub
Is there a simpler and faster way to do this?
You can create a datasource (using a linked server or OPENROWSET) and just do a SELECT on SQL Server side:
SELECT *
INTO mytable
FROM OPENROWSET('Microsoft.Jet.OLEDB.4.0', 'Excel 8.0;Database=C:\myfile.xls', 'SELECT * FROM [Sheet$]')
The path with the Excel workbook should be accessible by the SQL Server.
Here is a KB article on how to do this.
Solution using Import Excel File
Since you have a restriction on not forcing the user to save the file, and most proposed solutions require that the file be saved, I would use VBA to
create a workbook object in memory
copy paste from active worksheet to workbook
SAVEAS workbook to a temp file name (TempExcelFile.xls) and path
use Quassnoi's method above to import into SQL Server
overwrite TempExcelFile.xls with zeroes
delete TempExcelFile.xls
This way the user will not be forced to save the worksheet, and they will be prompted to save it just like they would during the natural course of their work.
To facilitate multiple active worksheets being simultaneously imported, I would use either a timestamp or a GUID to create the file names.
Solution using ADO Queries
Have your users create their workbooks from a template that has VBA macros built in Make sure you protect the workbook to prevent unauthorized access to the macros/code behind.
Execute a macro that will loop through the rows and columns, read the cell values, build a SQL query and execute it - use stored procedures with parameters to prevent injection attacks.
Generally I import large Excel files using SSIS.
Have you tried just putting an ActiveWorkBook.SaveAs("some temp filename") at the top, and importing from that? I do something similar with a macro to FTP the contents of the ActiveSheet to a remote server and it works.

Resources