Dynamically create Datasheet from SQL query with VBA in Ms Office Access - vba

I have Access database project connected with Ms SQL Server. Data (tables) is stored in the Ms SQL server and Forms and Reports are stored in the Access .ADP file. It is not possible to create Queries, Tables, Views using Design view but Tables and Views can be created using SQL queries and stored on the server. I don't have Ms SQL Server Management Studio and I can't install it in my computer in my office.
So, what I want is to get a dynamically generated Datasheet of a SELECT SQL query to see results temporarily for data analysis. I have placed a textbox and a button in a form and want to display a datasheet containing the result of the SQL query written in the textbox when the button is clicked.
I tried this but it is not working for me and doesn't seems what I want:
MS Access VBA - display dynamically built SQL results in datasheet subform
I also tried by assigning query to Recordsource property of a form. It is showing blank datasheet, but the navigation pane below the datasheet is showing the actual number of records retrieved. So, it is working but not showing the data.
I tried (from http://www.pcreview.co.uk/forums/create-query-dynamically-vba-t3146896.html):
Dim db As DAO.Database
Dim qd As DAO.QueryDef
Dim strSQL As String
Set db = CurrentDb
strSQL = "select * from analysts"
Set qd = db.CreateQueryDef("NewQueryName", strSQL)
DoCmd.OpenQuery "db.NewQueryName"
It is showing run-time error 91, Object variable or With block variable not set on the line Set qd = db....
And also (from the same page):
Dim strSql As String
strSql = "select * from analysts"
CurrentDb.QueryDefs("qryExport").SQL = strSql
DoCmd.OpenQuery "qryExport"
Returning same error on the line CurrentDb.QueryDefs.....
Any idea or workaround?

This doesn't seems efficient, but it is working and a bit satisfactory:
DoCmd.RunSQL "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS " & _
"WHERE TABLE_NAME = 'tv') DROP VIEW tv"
DoCmd.RunSQL "create view tv as " & txtQry
DoCmd.OpenView "tv"
Here I am creating a temporary VIEW (tv) in a button's click event. Before creating the view, I am checking that if a view with the same name exist or not; and if it exist then delete it so that a new view can be created with the same name with different query.

Access ADPs are a whole different beast than the normal MDB that you are probably used to working with. DAO isn't generally used in ADPs, you probably had to add a reference to DAO to get any part of the code above to work. ADPs are designed around using ADO to interact with the source database. If you just want to open a recordset to your data, use something along these lines.
Dim strSql As String
Dim rs as ADODB.Recordset
strSql = "select * from analysts"
set rs = New ADODB.Recordset
Set rs.ActiveConnection = CurrentProject.Connection
rs.Source = strsql
rs.Open
You can then interact with that recordset. If you want to bind that recordset to a form so that you can view the data, you can use:
Set Me.Recordset = rs

This is working how I want:
Dim frm As Form ' create a form dynamically
Set frm = CreateForm
Dim rs As New ADODB.Recordset
rs.Open Replace(txtQry, vbCrLf, " "), CurrentProject.Connection ' replace "enters" (vbCrLf) with space because it was throwing error while executing query!
Dim c As Control
For Each f In rs.Fields
Set c = CreateControl(frm.Name, IIf(f.Type = 11, acCheckBox, acTextBox)) ' if field type is "bit", add checkbox; for other datatypes, add textbox
c.ControlSource = f.Name
c.Name = f.Name ' sets column header to the same name as of field's name
Next
rs.Close
frm.RecordSource = txtQry
DoCmd.OpenForm frm.Name, acFormDS ' open form in "DataSheet" view otherwise it will be in "Form" view
This code is placed in a button's click event.
Other possible datatypes for rs.Fields(x).Type (here, f.Type) are (3 = int, 200 = varchar)

Related

Refresh forms recordset source with custom SQL query keeping current selection after closing second form

I have 2 forms in MS Access. One is an overview form as an endless form, the other shows details of the single records.
The overview form gets its content by a custom SQL passthrough query on opening:
Dim strSQL As String
strSQL = "SELECT DISTINCT tbl_Parts.strPart, Title, Comment FROM [tbl_Parts] inner JOIN [tbl_PartModules] ON tbl_Parts.strPart = tbl_PartModules.strPart ORDER BY tbl_Parts.strPart DESC"
Dim qdf As DAO.QueryDef, rst As DAO.Recordset
Set qdf = CurrentDb.CreateQueryDef("")
Dim strTmpConnQDF As String
strTmpConnQDF = CStr(Application.TempVars("tempvar_StrCnxn"))
qdf.Connect = strTmpConnQDF
qdf.sql = strSQL
qdf.ReturnsRecords = True
Set rst = qdf.OpenRecordset
Set Forms![frm_PartsOverview].Recordset = rst
Forms![frm_PartsOverview].Requery
'No rst.Close to have the data still in the Form!
Set qdf = Nothing
Within that overview, buttons can be used to change the underlying SQL query to show different records.
Each record has a "show details" button in the endless form which opens the detail form.
Within the detail form, I can change the data of the underlying record. After closing this form, I want to keep all settings (record selection, SQL query data) that were before opening the detail form. But I also want to update the underlying data, showing the changes made in the detail form also on the overview form.
I tried this code with the "hide" button of the detail form, but this does not work:
Forms!frm_PartsDetail.Visible = False
Forms!frm_PartsOverview.Recalc
Forms!frm_PartsOverview.Refresh
Forms!frm_PartsOverview.Requery
I tried all three variants (Recalc, Frequery, Refresh) but none of them did the trick.
I guess its because of the way I assign the SQL query as the recordsource of the form.
But how to update the recordsource, keeping all settings like they were before entering the detail form?
Well, you can use a me.Refresh - that will (should) show updates to the form. (but, the fact of it being basedon a pt query probably will effect this. I suggest changing to a linked view - thus no pt query required and thus access can stay on the given row, and you can use a me.refresh. (with a PT query and you having assigned the forms data source "one time", then me.refresh is unlikely to work.
So, I suggest using a linked view for that form. (and performance will be just as good at the pt query - and your me.Refresh will work (to show any changes made to the table).
the other way? Grab the current row (PK) before you do the me.Requery. Then do a find first on the pk row, and jump back to the row in question.
So, ignoring you have a sub form etc.
Then this code:
Dim PK As Long
PK = Me!ID
Me.Requery
Dim rst As DAO.Recordset
Set rst = Me.Recordset
rst.FindFirst ("ID = " & PK)
However, as noted, if you base that form on a linked view, and not set the datasource as you do, then a me.Refresh will show any changes made, and also stay on that current row.
So, in your case, to re-calc, re-display, and show any updates, and return to current row you were on?
"air code warning"
Dim PK As Long
Dim f As Form
Dim rst As DAO.Recordset
Set f = Forms!frm_PartsDetail
PK = f!ID
f.Requery
Set rst = f.Recordset
rst.FindFirst ("ID = " & PK)

Populate results from SQL Server using Recordset in MS Access using VBA code

I am not sure how I can put the results from Record Set into a query or result pane in MS Access using VBA code. The results in the recordset are from SQL Server, so I want to display the results in MS Access. I need to do it this way, is that possible? I would think I need to do something where are x's are.
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim strConnString As String
strConnString = "Provider=SQLNCLI11;Server=SRV;Database=Staging;Trusted_Connection=yes;"
Set conn = New ADODB.Connection
conn.Open strConnString
Set rs = conn.Execute("Select * from MSAccess_APP_ComplianceDashBoard ")
XXXXXXXXXXXXXXXXXX
rs.Close
Assuming a continues form,or a multiple items form?
In the forms on-load event just go:
me.ReocrdSource = "Select * from MSAccess_APP_ComplianceDashBoard"
It is of course assumed that you have a linked table to SQL server with the above name.
In fact, you can set the forms record source in design mode and as a result you need zero lines of code.
So, setup a linked table - you not need to write any code.
Create a pass-through query, put your connection string there, and do whatever you your needs over this query.

In Excel VBA run SQL "SELECT ... INTO ... IN ...." Statement

I can't seem to find any good reference or example of how to get this to work. I have a database which is stored on an AS/400 (my local MS Access database [stored on a network drive] has linked tables to the 400, using ODBC/DSN). My utility works just fine passing SQL statements to through Access to retrieve data from the 400 using the linked tables. The problem is that with some of the larger reports and the fact that the 400 is several states away, it can take several hours to run the reports. The settled on solution to this is to create a local "copy" of the tables needed with just the data set that is relevant to the reports, which is a considerably smaller data set. Obviously this has the down side of not being "live" data but I can live with that. Ultimately what I want to do is gather the relevant data from the linked table and save it to separate database that is local to the client so that it could be used if offsite/offline and to increase the speed of the report.
network location stored database = DB1 (Tabled linked to AS/400)
local client stored database = DB2 (relevant data set created by below SQL, non-linked tables named the same as the linked tables)
Below is the SQL statement that I'm trying to get to work using VBA & DAO
SELECT
DB1_TABLEA.FIELD1,
DB1_TABLEA.FIELD2,
DB1_TABLEA.FIELD3,
DB1_TABLEA.FIELD4,
DB1_TABLEA.FIELD5,
DB1_TABLEA.FIELD6,
DB1_TABLEA.FIELD7,
DB1_TABLEA.FIELD8
INTO
DB1_TABLEA IN 'Local_DB_Copy.accdb' <== Creating non-linked copy
FROM
DB1_TABLEA
WHERE
(
((DB1_TABLEA.FIELD4) Like 99999)
AND
((DB1_TABLEA.FIELD6)="02" Or (DB1_TABLEA.FIELD6)="22")
)
;
I already have my program working fine and returning/processing data from the AS/400 DB. I just need to be able to get the above to work so that people have the option to run a local copy that will process much faster.
Below is the code that I tried, but of course it fails or I wouldn't be here.
Sub gCreateLocalDBTables()
Dim DBPath As String
Dim LocalDBPath As String
Dim sSQL As String
Dim DB As DAO.Database
Dim DB2 As DAO.Database
Dim RS As DAO.Recordset
LocalDBPath = "AS400_Local.accdb"
sSQL = "SELECT DB1_TABLEA.FIELD1, DB1_TABLEA.FIELD2, DB1_TABLEA.FIELD3, DB1_TABLEA.FIELD4, DB1_TABLEA.FIELD5, DB1_TABLEA.FIELD6, DB1_TABLEA.FIELD7, DB1_TABLEA.FIELD8 INTO DB2_TABLEA IN '" & LocalDBPath & "' FROM DB1_TABLEA WHERE (((DB1_TABLEA.FIELD4) Like 99999) AND ((DB1_TABLEA.FIELD6)='02' Or (DB1_TABLEA.FIELD6)='22'));"
Set DB = OpenDatabase(LocalDBPath, False, False)
DB.TableDefs.Delete ("DB2_TABLEA")
DB.Close
DBPath = Interaction.GetSetting("Cust_Tools", "Settings\Report_Planning", "400DB_Location")
Set DB2 = OpenDatabase(DBPath, False, False)
Set RS = DB2.OpenRecordset(sSQL)
RS.Close
DB2.Close
Set RS = Nothing
Set DB = Nothing
Set DB2 = Nothing
End Sub
I know the SQL works as I have tested it from inside MS Access. I just can't find info on how to get it to work being passed from Excel VBA
You cannot assign an action query like a make-table query (i.e., SELECT with INTO call) to a recordset. Consider executing your DROP and SELECT ... INTO action queries prior to opening recordset on the local table. Also, it is unclear why you are opening a second database or what the path points to. Below opens a recordset on the mainframe data:
Set DB = OpenDatabase(LocalDBPath, False, False)
DB.Execute "DROP TABLE DB2_TABLEA", dbFailOnError
DB.Execute sSQL, dbFailOnError
Set RS = DB.OpenRecordset("SELECT * FROM DB2_TABLEA")
Furthermore, the IN clause in your make-table query is unnecessary as you are currently connected to the very database you are running the action on. Simply remove it ('" & LocalDBPath & "'). Also, LIKE expressions without wildcards and on numbers should be replaced with =
SELECT
DB1_TABLEA.FIELD1,
DB1_TABLEA.FIELD2,
DB1_TABLEA.FIELD3,
DB1_TABLEA.FIELD4,
DB1_TABLEA.FIELD5,
DB1_TABLEA.FIELD6,
DB1_TABLEA.FIELD7,
DB1_TABLEA.FIELD8
INTO
DB2_TABLEA
FROM
DB1_TABLEA
WHERE
(
((DB1_TABLEA.FIELD4) = 99999)
AND
((DB1_TABLEA.FIELD6)='02' OR (DB1_TABLEA.FIELD6)='22')
)
;
In fact, consider saving the query inside the MS Access database (Ribbon -> Create -> Query Design -> SQL View) and call it as a named object and avoid any long SQL in VBA.
DB.Execute "DROP TABLE DB2_TABLEA", dbFailOnError
DB.Execute "mySavedQuery", dbFailOnError
Set RS = DB.OpenRecordset("SELECT * FROM DB2_TABLEA")

binding forms to a recordset which is opening a table on local db

I am trying to show data in a recordset by binding it to a form in ms-access 2010. recordset is opening a query over currentdb (not external). Here's my code, its executing on the Form_Load() event:
Dim rs as Recordset
Set rs = CurrentDb.OpenRecordset("SELECT Employee.* FROM Employee", dbOpenDynaset)
Set Me.Recordset = rs
The form in my opinion is not binding to the recordset. I tried searching this for over a day but though I'm setting my controlsource properties properly I cannot figure out why nothing is getting displayed.
My recordset as I said is NOT and ADODB recordset!! its just a recordset object grabbing data from the local database (which has linked tables).
p.s: I tried executing the same in the Form on Open event but that caused ms-access to crash
So apparently I led you astray... while "SELECT 1 AS test" is a perfectly valid query, forms don't seem to like it. Instead, try this.
Create a table called "tblTest", add a single Text field "test" and add a few items.
Create a blank continuous form and call it "frmTest". Add a single textbox control and call it "txtTest".
Add a Form_Open event code builder and define it like so:
Private Sub Form_Open(Cancel As Integer)
Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset("SELECT * from tblTest")
Set Me.Recordset = rs
Me.txtTest.ControlSource = "test"
End Sub
Does that work?

How to view a recordset in an access table by means of vba?

With help of the embedded access vb editor i've written a small code to analyse the field values of my database, and want to finally view the recordsets in a table inside the opened access. As a newbie i can only use Debug.Print to display the field names. Could anyone of you tell me with which statements/commands i can execute my SQL String in order to view the result recordset with values?
Debug.Print FinalSQLString
Here is the basic recipe:
Dim db As Database
Dim rs As Recordset
Set db = CurrentDb
Set rs = db.OpenRecordset("SELECT * FROM myTable")
Know that you are using Jet Data Access Objects (DAO) with Access - google that for details.
Expression (rs.BOF and rs.EOF) = True indicates there were no rows.
Use rs.MoveFirst, rs.MoveNext to go to the first and next rows. Test rs.EOF after rs.MoveNext; when True, last row was already processed.
rs(FieldName) returns the value of the column named FieldName (a string expression).
rs(1) returns the value of the second column.
When done, rs.Close.
There is no way to hand Access the RecordSet and have it displayed in a Datasheet view. Instead, you will have to create a QueryDef object and use it to perform the query and display the Datasheet view of the results:
Dim qd As QueryDef
On Error Resume Next
CurrentDb.QueryDefs.Delete "temp"
On Error GoTo 0
Set qd = db.CreateQueryDef("temp", "SELECT * FROM myTable")
DoCmd.OpenQuery "temp", acViewNormal, acEdit
As far as I know, there is no way to display a datasheet containing a VBA instance of a recordset. If the source of your recordset is strQSL, you could however create a table with your results, and open that one, or more elegantly, create queryDef and open it:
Sub ShowQd(strQdName As String, strSql As String)
'creates queryDef and display it in a datasheet'
Dim qd As DAO.QueryDef
Set qd = CurrentDb.CreateQueryDef(strQdName)
With qd
.ReturnsRecords = True
.SQL = strSql
End With
DoCmd.OpenQuery strQdName
End Sub
If you focus on displaying things, you could also put a ListBox in a form, set its Number of columns to the number of fields returned by your query (qd.Fields.Count), and set your strSql as the RowSource of the ListBox. AND... if you put all your related code in that form, you now have a form that you can import in any db to quickly display what you want :)
Good luck !