Using ADO/DAO Connection to Download data from SQL Server - sql

I am trying to figure out how to download using an ADO/DAO connection in Access VBA to get the contents of a table from SQL server. I am trying to avoid using a linked table because the DB requires a password and I keep running into issues with getting it to not ask for the login info. Are there any ideas or references for me to start with on this matter?

It appears either way you'll need to provide SQL credentials.
There's more involved without linking a table, basically you'd want a recordset for the source and the "target" table to iterate over.
targetrs = CurrentDb.OpenRecordset("Target", dbOpenTable)
Dim Con As New ADODB.Connection
Dim sqlStr As String
Con.Open _
"Provider = sqloledb;" & _
"Data Source=SqlServer;" & _
"Initial Catalog=MyDB;" & _
"User ID=sa;" & _
"Password=p#ssW0rd;"
Dim rsSource As New ADODB.Recordset
rsSource.Open "select * from SOURCE", Con
do until rsSource.eof
targetrs.addnew
for each field in rsSource
targetrs.fields(field.Name) = rsSource.fields(field.Name)
next
targetrs.update
rssource.movenext
loop
Since you still have to have the credentials, you could dynamically link the table instead:
docmd.TransferDatabase acLink,"ODBC Database",
"ODBC;Driver={SQL Server};Server=MySQLServer;Database=MYSQLDB;
Uid=USER;Pwd=PASSWORD",acTable,"SQLtable","MyAccessTable"

Use of a linked table does not require you store or have the user password in that linked table.
If you execute a SINGLE logon at application startup then all linked tables will work.
Linked tables work WITHOUT a prompt for user or password.
Linked tables work WITHOUT you having to store the user ID or password in the link.
Access will cache the user name + password if you logon as per the instructions here:
http://blogs.office.com/b/microsoft-access/archive/2011/04/08/power-tip-improve-the-security-of-database-connections.aspx
So to download a table to a local, then you ONLY need this code:
For a new local table (create table query):
CurrentDb.Execute "SELECT * INTO LocalTableCreate FROM ServerTable"
Append to existing table:
CurrentDb.Execute "INSERT INTO LocalTable SELECT * FROM ServerTable"
And if some really strange reason and desire exists create and promote world poverty and do things the hard way like a turtle with time to waste and not use a linked table?
Well you could create a linked table via the “transfer database” command. It is only one extra line of code in front of the above code and then AGAIN the above two examples would work fine.
However I see little if any advantage to creating + deleting a linked table.
I suppose for reasons of performance or perhaps for security or the legitimate reason of you not knowing the table ahead of time? Then I would suggest you use a saved a pass-though query as performance will be even faster.
So you can use this code:
Dim qdfPass As DAO.QueryDef
Set qdfPass = CurrentDb.QueryDefs("MyPass")
qdfPass.SQL = "select * from dbo.MyTable;"
CurrentDb.Execute "INSERT INTO LocalTable SELECT * FROM MyPass”
Note that the sql used in above qerydef MUST be native T-SQL and can be a view or even a store procedure like:
qdfPass.SQL = "exec sp_myCoolStoreProc;"
And the stored procedure can even be passed a parameter like this:
qdfPass.SQL = "exec sp_myCoolStoreProc " & strMyParam
and then :
CurrentDb.Execute "INSERT INTO LocalTable SELECT * FROM MyPass”
So we can even use a select into/append from a store procedure by doing the above and the table/sql server side is dynamic or can even be a stored procedure. Again VERY little code.
I would suggest you avoid the idea proposed here to write recordset looping code unless one really has the desire to write looping code when none is required. And things like PK would have to be dealt with separate in code if you use such loops since the local pk column may need to be skipped (you simply leave that column out of the select SQL).
Note again that the connection string saved for the pass-though query does NOT require the user ID and password by using the above link showing how to “logon” to SQL Server. And if the table is known, then again a saved table link or pass-though query will suffice here.

Related

Getting the number of affected records by an action query in VBA

I'm wondering if it is possible to pick a variable which is being used by MS Access when inserting a specific amount of new data rows to a table which happens during a VBA runtime.
Screenshot of the MS Access Notification (in German):
MS Access notifies me here in that case that in the middle of a running VBA script 2327 new data rows are being added to the table. I'm not a programmer but I feel that this variable must be stored somewhere (at least temporary).
Does anyone has an idea how to pick/access this number from the window when it appears and then store it into an VBA Integer for further use in the future?
Update:
Basically, I have a main database which has to be updated every day by an import file.
In the first step there is a check for already existing datasets which will therefore only updated by an UPDATE-Query.
UPDATE ReiseMaster
INNER JOIN Update_Import
ON(ReiseMaster.Col1 = Update_Import.Col1)
SET ReiseMaster.Col2 = Update_Import.Col2,
ReiseMaster.Col3 = Update_Import.Col3;
Then, in the second step new datasets which are not existing in the main database will be inserted into it will all the information they contain. This is the SQL-Query which is responsible for appending new data rows during the VBA procedure:
INSERT INTO ReiseMaster ([Col1],[Col2],[Col3])
SELECT [Col1],[Col2],[Col3] FROM Update_Import
WHERE NOT EXISTS(SELECT 1 FROM ReiseMaster
WHERE Update_Import.[Col1] = ReiseMaster.[Col1]);
I am struggling with identifying the amount of new datasets after the procedure, which is in fact already somehow determined by MS Access itself (see Pop-Up). So my idea was to just use the number which is already determined by MS Access.
All SQL-Queries are stored in a string and run by the "DoCmd.RunSQL" command.
Using DAO, it's really easy to get the number of records affected after executing a query.
You can just use the Database.RecordsAffected property:
Dim db As DAO.Database
Set db = CurrentDb 'Required, don't use CurrentDb.Execute else this won't work
Dim strSQL as String
strSQL = "UPDATE ReiseMaster INNER JOIN Update_Import" 'and the rest of your query
db.Execute strSQL, dbFailOnError 'Execute the query using Database.Execute
Dim recordsAffected As Long
recordsAffected = db.RecordsAffected 'Get the number of records affected
However, this won't allow you to see it before committing the query. To see it and display a prompt, you can use transactions (short sample here), and rollback the transaction if the user doesn't choose to commit.

How to create a live connection between MS Access and SQL Server?

The data I am using is stored in a SQL database and an Access database. Regular data entry needs to be made in the Access database, however, I would like to write my queries in SQL Management Studio. I have connected the two databases using an ODBC connection and I can now view the tables in Management studio but the connection is not live, so any updates in Access are not reflected in the tables in Management Studio.
You can create a linked table from Access to SQL Server (INSERT INTO). You can use SQL or VBA to export all data from Access to SQL Server.
Here is one idea.
Sub modExportToPG()
Dim rs As DAO.Recordset
'We only want to export physical user defined visible and hidden tables
Set rs = CurrentDb.OpenRecordset("SELECT Name " & _
" FROM MSysObjects " & _
" WHERE Type=1 AND Flags < 9 ORDER BY Name;")
Do Until rs.EOF
'export the tables but export as lower case table names
DoCmd.TransferDatabase acExport, "ODBC Database" _
, "ODBC;DRIVER={PostgreSQL Unicode};DATABASE=mydb;SERVER=myserver;PORT=5432;UID=myuser;PWD=mypwd" _
, acTable, rs("Name"), LCase(rs("Name"))
rs.MoveNext
Loop
rs.Close
End Sub
Also, see this for more ideas.
https://www.mssqltips.com/sqlservertip/1480/configure-microsoft-access-linked-tables-with-a-sql-server-database/
Came upon this s little late but I am in the midst of working through a project where we update SQL side tables at the same time that we update local and shared (server side) Access tables. It is a fairly extensive application, but the SQL updates are relatively easy and there are options. The application integrates with a Point of Sale back office server and we are sensitive to the data that gets updated instantly, so those items get tucked into batches that require approval before they get "posted" to SQL. Those items that are not sensitive we post immediately using the afterupdate property for each field on the main form using the following code (sample only):
Private Sub Field.AfterUpdate
Dim Var1 as string
Dim Var2 as string
Dim Var3 as string
'Capture Before and after values for the field (as well as using the_
newvalue to post to SQL, we store these separately as audit records_
so we capture both old and new).
Var1 = Me.Field.OldValue
Var2 = Me.Field.NewValue
Var3 = Me.PID.Text
'Execute the SQL update
DoCmd.DbExecute "Update dbo.SQLTABLE SET SQLField = '" & Var2 & "'_
WHERE SQLTABLE.PID = '" & Var3 & "'
End Sub
Note: PID is a record id attached to every record in the access form that matches a corresponding record in a SQL Table (primary key).
It is a lot of work if you have a number of fields to post but it's as good as it gets using linked tables in my experience. This is a simple example and ours are far more extensive, but it does the trick.

Import partial table from remote DB into a local microsoft access DB

Problem:
I'm trying to import a portion of a table from a remote informix db into an access 2016 database. The table to be partially imported contains account information and is > 2 GB (which as I understand it is the max size of a access db).
I only need information for the last year which is much less than 2 GB but the information must be updated at the end of each day.
Two solutions I have seen online are:
(A) Link the tables:
I'm reluctant to do this for two reasons:
1) I don't want additional load placed on the server everytime the enduser of the access db runs a report.
2) I don't want any changes carried out in the access DB to carry over to the live informix DB (this is absolutely cruical)
(B) Copy over the entire table via the access GUI
I don't think this is a viable solution for the following reasons:
1) The table is over 2GB
2) Reports must be ran on the table everyday with daily updated information. This would require pulling the entire informix table into access everyday.
Work So far:
I have succefully connected to the informix db from access via creating a dsn for the informix db.
I have also sucessfully imported a smaller table from the informix server via VBA with the following code:
DoCmd.TransferDatabase acImport, "ODBC Database", "ODBC;DSN=My_DSN_Name;UID=odbc;PWD=My_PWD;LANGUAGE=us_english;" & "DATABASE = My_DB_Name", acTable, "My_Destination_Tbl_Name", "TestTableImport", False, True
My ideal solution would see the enduser of the access db press a button that updates the local access copy of the db with all changes made to the informix db since the last update.
I have limited experience with access and VBA so would really appreciate any pointers on how I should proceed. Perhaps I have overlooked something simple?
Thanks for reading and any advice you can provide,
John
Ummmm.... assuming the data in the table is static (no updates to column data, just new rows) you could do something like importing whatever subset of data you want in Access and then every time you click a button run a macro to check what is the latest row in the access table.
Use that info to select only new rows from the ODBC source and insert them in the access table.
I tried this with Access 2013:
Sub test()
Dim cnDB As New ADODB.Connection
Dim rsRecords As New ADODB.Recordset
' find last row
Set db = CurrentDb
strSQL = "SELECT TOP 1 empno As lastemp FROM informix_employee ORDER BY empno DESC"
Set rs = db.OpenRecordset(strSQL, dbOpenSnapshot)
lastemp = rs("lastemp")
rs.Close
' get new rows from odbc source
DoCmd.SetWarnings False
cnDB.Open "DSN=ids1210;uid=informix;pwd=password"
rsRecords.Open "Select * from employee where empno>" & lastemp, cnDB
Do While Not rsRecords.EOF
strNaam = rsRecords.Fields(0).Value
rsRecords.MoveNext
' update access table
DoCmd.RunSQL ("insert into informix_employee (empno) values ('" + strNaam + "')")
Loop
rsRecords.Close
Set rsRecords = Nothing
cnDB.Close
Set cnDB = Nothing
db.Close
End Sub
It's a basic example, but should give you an idea of how to do it.
The code above inserts only one column per row (empno), you will need go through your table and do the insert with all the columns (basically create the insert ............. statement for your table)
'informix_employee' is the Access table.
'employee' is the Informix table.
Also, you need to reference ADO, e.g. 'Microsoft ActiveX Data Objects 6.1' to get it working, otherwise it will fail as the ADODB object wont be there.
If the Informix table does get some column updates (not just new rows) it would be quite difficult, as nothing (IDS/ODBC/Access) will be able to tell you which rows were updated without getting the data and comparing it with the previous set.

MS Access query with dynamic from statements

Ok this is vexing me. I have a query I created with an in statement in the from clause. What I am looking to do is have a global variable populate that from statement. Example
Select *
Form query1 in <Global Variable filename>
What is going on is I link to a file that has hundreds of linked table and queries in it. My database only has a few select queries and link table to different database. I did not want to re-build all the queries and linked table is my database. The issue is the file with all the links changes name once a month. I just want to read a text file with the current name of the database in it so I do not have to keep changing my queries every time the database name changes. Also this has to a query since I have other queries using the externally linked query.
I have one suggestion, but its pretty kludgy.
Rewrite the query on the fly with VBA call
Private Sub update_qtest()
Dim db As Database
Dim qd As QueryDef
Set db = CurrentDb
Set qd = db.QueryDefs("qtest")
qd.SQL = "SELECT * from query1 in " & g_file_name
End Sub
As I said, it's kludgy, but I don't think there's a way to pass the from clause as a parameter.
Another way to do this would be to just use the same file name each month so you wouldn't have to change anything in your Access app at all. You could easily code copying the file to the standard name over top of the previous copy (you'd have to delete it before copying, of course), which would retain the history.

Run Query Against ODBC Connected Table VBA

I have a table (readings) already connected by ODBC in Access that opens very quickly when I click on it.
However, when I try to run this in VBA I it locks up and never displays anything:
Dim strSql As String
strSql = "SELECT readings.ids " & _
"INTO ids_temp " & _
"FROM readings " & _
"WHERE readings.ids > 1234;" //This is id in middle of list
DoCmd.SetWarnings False
DoCmd.RunSQL strSql
DoCmd.SetWarnings True
For some reason this crashes the whole system. Any ideas?
Rather than using DoCmd, t's usually handled by your existing connection to create a Command object, which accepts SQL statements to use with the Command.Execute method.
Reading the documentation for DoCmd, it appears to primarily be intended for eexecuting Macros from the Access UI menus.
Does you Database have ids_temp table locally? If ids_temp table is Linked table it will delete the table, because select into CREATES NEW TABLE. If you want to add to table try INSERT INTO command. You can clean table before inserting the data.
So the error was actually my fault the id I was using was causing the Query to return about 6 million results. But, this method actually works great, I just create the table and link a list box on a different form to the table, then I just show the form. I do some closes and updates in between but overall it seems to work well. Thanks for the help
Let me say that DoCmd.RunSQL is never advisable, particularly with SetWarnings turned OFF, as you have no idea whether the result is what you expect or not. You've told VBA not to report errors, so you never know if all the records were inserted or not.
It's very easy to replace DoCmd.RunSQL with my SQLRun() function, posted here:
How can I get a value from the update query prompt in Access VBA?