I would like to create a database in SQL server using VBA (Excel) just the first time that I will run the code. So the second time I run the code, the database will exist, and it will not create another one.
#abarisone
`Public Sub CreateDBTable()
Dim dbConnectStr As String
Dim Catalog As Object
Dim cnt As ADODB.Connection
Dim dbName As String
Dim tblName As String, ServerName As String, UserID As String, pw As String
tblName = shControl.Range("B5") 'Table Name
ServerName = "SERVICESWS15" 'Enter Server Name or IP
dbName = shControl.Range("B4") 'Enter Database Name
UserID = "" 'Leave blank for Windows Authentification
pw = "" 'Leave blank for Windows Authentification
dbConnectStr = "Provider=SQLOLEDB;Data Source=" & ServerName & ";Initial Catalog=" & dbName & ";User Id=" & UserID & ";Password=" & pw & ";"
Set Catalog = CreateObject("ADOX.Catalog")
Catalog.Create dbConnectStr
Set Catalog = Nothing
Set cnt = New ADODB.Connection
With cnt
.Open dbConnectStr
.Execute "CREATE TABLE " & tblName & " (KEY nvarchar(100) NOT NULL, " & _
"Date nvarchar(100) NOT NULL, " & _
"PRIMARY KEY (KEY));"
End With
Set cnt = Nothing
End Sub
`
There is an error in this line:
Catalog.Create dbConnectStr
Error: No such interface supported
It's not very complicated. First make sure that you are referring to the appropriate ADO library like here on the screenshot.
Then your have certain building blocks you will have to use: first make a Connection object (with a connection string), then make a Command object, and last but not least use the ExecuteNonQuery method of Command on your connection object. It does what the name says: executes an SQL command without having a RecordSet as a return object. See examples in the documentation starting from here.
I have not tried it before, but for this to happen without error, you will probably have to set your connection string to the system database called "Master" if working on MS SQL Server.
Of course, the SQL commands you will have to execute are (1) check if the database exists, (2) create db and tables if not.
Then you can create your "normal" Connection object to your database.
WARNING: to be able to create a database, your technical user defined in the VBA script must have high (system admin) privileges which is definitely a HUGE RISK even if you protect your excel. If it's not a sandbox environment, DO NOT DO IT!
Related
My task is to add new records from an excel table to a Microsoft SQL Server table, and to do this, I was planning on using ADODB objects; however, my SQL statement is not executing, and I think it has something to do with my connection strings.
In my code, I wrote down the SQL statement that I plan on using in the end, but when I tried:
sql = "SELECT * FROM [Provider=SQLOLEDB;Data Source=hpwfh-ssql01; _
Initial Catalog=HPW DataIntegrated Security=SSPI;Trusted_Connection=Yes].Hubspot_Data"
(a simple select statement) it didn't even work.
Sub update1()
Dim cn, rs As Object, path As String, name As String, sql As String, file As String
path = "T:\Marketing\Data Analytics\Hubspot data for SQL"
name = "Hubspot_Data"
file = path & "\" & name & ".xlsx"
Set cn = CreateObject("ADODB.Connection")
With cn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.connectionstring = "Data Source=" & file & ";Extended Properties=""Excel 12.0 Xml;HDR=YES;Readonly=false;IMEX=0"";"
.Open
End With
sql = "INSERT INTO [Provider=SQLOLEDB;Data Source=hpwfh-ssql01;Initial Catalog=Hubspot_Data;Integrated Security=SSPI;Trusted_Connection=Yes].Hubspot_Data " & _
"SELECT * FROM (SELECT * FROM [Provider=SQLOLEDB;Data Source=hpwfh-ssql01;Initial Catalog=Hubspot_Data;Integrated Security=SSPI;Trusted_Connection=Yes].Hubspot_Data" & _
"EXCEPT SELECT * FROM [hubspot-crm-exports-sql-data-20$])"
Set rs = cn.Execute(sql)
End Sub
Just a side note: the table is named the same thing as the database
For this code, I have gotten three different errors:
The Microsoft Access database engine could not fin the object 'Area' Make
sure the object exists and that you spell its name and the path name
correctly. (And I did not misspell Hubspot_Data)
External table is not in the expected format.
The Microsoft Acess database engine cannot open or write to the file
(My File Path)'\My Documents\Provider=SQLOLEDB.XLSX'. It is already opened
exclusively by another use, or you need permission to view and write its
data.
Clearly the computer is going to the wrong place to retrieve the table it needs, and I have no idea where I went wrong. Thanks for the help.
First of all you need 2 connections - one for SQLSvr and one for Excel.
Then query your source (Excel) and do a separate insert into SQLSvr. You are not going to be able to mix these into one query.
Sub SelectInsert()
Dim cn As Object, rs As Object, sql As String
Dim conSQL As Object, sInsertSQL As String
'---Connecting to the Data Source---
Set cn = CreateObject("ADODB.Connection")
With cn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=" & ThisWorkbook.Path & "\" & ThisWorkbook.Name & ";" & "Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
.Open
End With
Set conSQL = CreateObject("ADODB.Connection")
With cn
.Provider = "SQLOLEDB"
.ConnectionString = "Server=myServerAddress;Database=myDataBase;Trusted_Connection=True;"
.Open
End With
'---Run the SQL SELECT Query---
sql = "SELECT * FROM [Sheet1$]"
Set rs = cn.Execute(sql)
Do 'the insert. Each rs(n) represents an Excel column.
sInsertSQL = "INSERT INTO table VALUES(" & rs(0) & ";" & rs(1) & ";" & rs(2) & ")"
conSQL.Execute sInsertSQL
rs.MoveNext
Loop Until rs.EOF
'---Clean up---
rs.Close
cn.Close
conSQL.Close
Set cn = Nothing
Set conSQL = Nothing
Set rs = Nothing
End Sub
get properties of your database from "SQL Server Object explorer" and copy the exact same connection string. then copy it to the "appsettings.json" file of your project. It looks like this :
"connectionStrings": {
"ApiDbConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ApiDB;Trusted_Connection=True;"
}
then you need to create an object in your connection string and open a connection to the database using that object, then write your SQL query to the database
We have moved some back-end data tables over from a network drive (mbd file) to being on an SQL Server database. Things mostly work great, but if staff are accessing things through a VPN (which slows things down a lot), then we get connection errors when we run reports that retrieve a lot of data. My guess is that I need to set a timeout to a larger value, and I did some research and it seems that I need to set the commandtimeout (or maybe query timeout?).
Below is the VBA code we use to connect the SQL Server tables/views to our Access front end from the SQL Server back end. Am I right that I likely need to specify a commandtimeout? Where in this would we add the commandtimeout (or other timeout) value?
Public Sub CreateSQLLinkedTable(strSourceTableName As String, strNewTableName As String)
'************************************************************************************
'* Create a linked table in the current database from a table in a different SQL Server file.
'* In: *
'* strNewTableName - name of linked table to create *
'* strSourceTableName - name of table in database *
'************************************************************************************
Setup:
Dim tdf As TableDef
Dim strConnect As String, strMsg As String
Dim myDB As Database
' set database vars
Set myDB = CurrentDb
Set tdf = myDB.CreateTableDef(strNewTableName)
MakeConnections:
On Error GoTo OnError
' turn system warnings off
DoCmd.SetWarnings False
' define connect string and source table
' We do not need to specify the username (Uid) and password (Pwd) in this connection
' string, because that information is already cached from the connection to the SQL
' Projects database that we created in CheckSQLConnection() that was run to check connection
' to the database. So here we can have a connection string without the Uid and Pwd.
With tdf
.Connect = "ODBC;Driver={SQL Server};" & _
"server=" & myServer & ";" & _
"database=" & mySQLDB & ";"
.SourceTableName = strSourceTableName
End With
' execute appending the table
myDB.TableDefs.Append tdf
' turn system warnings back on
DoCmd.SetWarnings True
ExitProgram:
' this block of code will run if there are no errors
Exit Sub
OnError:
' this block of code runs if there is an error, per On Error assignment above
' display error message with details
MsgBox "There was an error connecting to the SQL Server data source Projects. Error = " & err & ", Description: " & err.Description
'exit Projects
Call CloseFormsAndQuit
End Sub
There is an ODBC timeout property. Open the query in design view, and go to properties to see it. There is also an (ODBC) query timeout on the current database properties page. You can set it programmatically as well:
Dim objDB As DAO.Database
Set objDB = CurrentDb()
objDB.QueryTimeout = 120
http://www.geeksengine.com/article/how-to-change-timeout-value-for-access-sql.html
Also check the server configuration. There is a query timeout on server side.
I have a table in Access 2003 that is linked to a MS SQL server table.
I am able to go into the Access table, manually update it and see the changes
reflected in the MS Sql server table.
However, I'm unable to even just open the table and put the result in the record set in my VBA code.
Set rs = CurrentDb.OpenRecordset("dbo_tbl_EventLogs", dbOpenTable)
(rs is always at nothing)
The same line of code works on a 'local' Access table.
Consider using dbOpenDynaset option which is used by default for linked tables or stored queries whereas dbOpenTable is used for local tables as mentioned for OpenRecordset() method:
If you open a Recordset in a Microsoft Access workspace and you don't
specify a type, OpenRecordset creates a table-type Recordset, if
possible. If you specify a linked table or query, OpenRecordset
creates a dynaset-type Recordset.
Set rs = CurrentDb.OpenRecordset("dbo_tbl_EventLogs", dbOpenDynaset)
You need to connect to your DB using ADO. Is simple and easy. Just change to your info and enjoy it.
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
ServerName = "yourServer,yourPort(example: 1442)" 'Enter your server name and a port here
DatabaseName = "yourDBinTheServer" 'Enter your database name here
UserID = "yourUserID" 'Enter your user ID here
Password = "yourPassword" 'Enter your password here
Set rs = New ADODB.Recordset
Set cn = New ADODB.Connection
cn.Open "Driver={SQL Server};Server=" & ServerName & ";Database=" & DatabaseName & ";Uid=" & UserID & ";Pwd=" & Password & ";"
rs.open yourQueryAsString, cn
Make sure you store the user credentials used to connect to the database...
(attribute dbAttachSavePWD)
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.
I have a query against a linked table in MS Access that uses the getdate() function of SQL Server. However, I get this error when I attempt to run the query:
Undefined function GetDate in function
How do I create a linked table that allows the use of SQL Server T-SQL syntax? I see that this is called a pass through query but I don't know how to set it up to use the connection on the linked table as a pass through query.
Currently using Access 2010. The query is:
select getdate()
If it helps, I used the following vba code that generates the table link to SQL Server:
Function LinkTable(LinkedTableAlias As String, Server As String, Database As String, SourceTableName As String, OverwriteIfExists As Boolean, Username As String, Password As String)
'This method will also update the link if the underlying table definition has been modified.
If (InStr(1, LinkedTableAlias, "MSys") > 0) Then
Log "Skipping " & LinkedTableAlias
Exit Function
End If
'The overwrite parameter will cause it to re-map/refresh the link for LinktedTable Alias, but only if it was already a linked table.
' it will not overwrite an existing query or local table with the name specified in LinkedTableAlias.
'Links to a SQL Server table without the need to set up a DSN in the ODBC Console.
Dim tdfLinked As DAO.TableDef
' Open a database to which a linked table can be appended.
Dim dbsCurrent As Database
Set dbsCurrent = CurrentDb()
'Check for and deal with the scenario ofthe table alias already existing
If TableNameInUse(LinkedTableAlias) Then
'If InStr(dbsCurrent.TableDefs(LinkedTableAlias).Connect, "AccessBackup") Then
' Exit Function
'End If
If (Not OverwriteIfExists) Then
Log "Can't use name '" + LinkedTableAlias + "' because it would overwrite existing table."
Exit Function
End If
'delete existing table, but only if it is a linked table
'If IsLinkedTable(LinkedTableAlias) Then
dbsCurrent.TableDefs.Delete LinkedTableAlias
dbsCurrent.TableDefs.Refresh
'Else
' Log "Can't use name '" + LinkedTableAlias + "' because it would overwrite an existing query or local table."
' Exit Function
'End If
End If
'Create a linked table
Set tdfLinked = dbsCurrent.CreateTableDef(LinkedTableAlias)
tdfLinked.SourceTableName = SourceTableName
tdfLinked.Connect = "ODBC;DRIVER={SQL Server};SERVER=" & Server & ";DATABASE=" & Database & ";UID=" & Username & ";PWD=" & Password & ";"
On Error Resume Next
dbsCurrent.TableDefs.Append tdfLinked
If (err.Number = 3626) Then 'too many indexes on source table for Access
err.Clear
On Error GoTo 0
If LinkTable(LinkedTableAlias, Server, Database, "vw" & SourceTableName, OverwriteIfExists, Username, Password) Then
Log "Can't link directly to table '" + SourceTableName + "' because it contains too many indexes for Access to handle. Linked to view '" & "vw" & SourceTableName & "' instead."
LinkTable = True
Else
Log "Can't link table '" + SourceTableName + "' because it contains too many indexes for Access to handle. Create a view named '" & "vw" & SourceTableName & "' that selects all rows/columns from '" & SourceTableName & "' and try again to circumvent this."
LinkTable = False
End If
Exit Function
End If
On Error GoTo 0
'** Turn on error handling
On Error GoTo ErrorHandler:
tdfLinked.RefreshLink
LinkTable = True
Exit Function
ErrorHandler:
Log "refreshlink failed for " & tdfLinked.Name
LinkTable = True
I don't quite understand this statement:
How to I create a linked table that allows the use of SQL Server T-SQL
syntax?
But this is how you convert an existing MS Access querydef to a pass through query:
Go to design mode in the query, press the Query menu command, then SQL Specific then Pass Through
See this for screenshots.
http://www.mssqltips.com/sqlservertip/1482/microsoft-access-pass-through-queries-to-sql-server/
The reason why you are getting the error is that GETDATE() is not a function inside MSAccess. You probably need Now() to get the date and time or you may use Date() which provides the date
Here's a quick and dirty VBA way to create a pass-through query:
Set qdf = CurrentDb.CreateQueryDef("testqry")
' this is just your connection string
qdf.Connect = "ODBC;Driver={SQL Server};Server=MSSQL1; Database=MyDB;Trusted_Connection=Yes"
'anything here gets passed directly to and executed on the SQL Server
qdf.SQL = "select getdate()"
Set qdf = Nothing
Now you can use "testqry" as if it's any other Access query (as far as SELECTing from it goes, anyway)
Simple save your t-sql query as a pass-though
Select GetDate()
Then in VBA code, you can go:
TheSqlDate = currentdb.QueryDefs("qPass").OpenRecordset()(0)
Using ADO, and hardcoding connection strings, and the HUGE whacks of other code posted here is just a way to rack up billable hours and create world poveity. My posted solution IS ONLY ONE LINE OF CODE!