manipulate query result and display on report with VBA - ms-access

I want to manipulate data that I get from an sql query then write it to a report all this done with VBA in MS Access.
So first i need to get the data I need with this sql query
SELECT test.number_id FROM test WHERE ((test.number_id)>30));
need to save the output in a variable let say
Dim testVar As Int
and make my calculations
then i need to display the result in the report.
Anyone know if thats possible and how to do this???

You can set your SQL statement to a recordset and then manipulate the results in there.
Dim myR2 As Recordset
Dim strSQL as String
strSQL = "SELECT test.number_id FROM test WHERE test.number_id>30"
Set myR = CurrentDb.OpenRecordset("strSQL", dbOpenDynaset)
'Manipulate myR info here'
myR.MoveFirst 'so you start from the first record'
myR.MoveNext 'to move to the next record; handy in a loop'
myR.FindFirst 'find a record in that recordset'
myR![FieldName] 'to call upon that record's field'
'Or use CREATE statement to create a new table and generate a report from it'
Set myR = Nothing

Related

VBA Variables inside DoCmd.RunSQL

Just a question. Situation is as follows:
I have 15 make table queries that pulls data for a different submission clarification code that was used on a claim; i.e. Pull all claims where submission clarification code 5. As of right now I have a macro that will run all 15 queries, but each time I am required to type in the region I am filtering for due to the [What Region?] prompt I had put in the criteria field.
My question is:
Is it possible to use VBA to run all 15 queries using the DoCmd.RunSQL where I only have to type in the region number once and it will apply it to all queries?
My initial thoughts were I would have VBA prompt me for what region I'm filtering for, store that in a variable, and then use that variable in the SQL statement. But I'm not even sure if you can do that? Thanks in advance for any advice that may be given!
Update: So after reading a few threads, I created a mock database to try out some of the concepts and I think I might be on the right track?
Private Sub btnTest_Click()
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim SQLString As String
SQLString = "SELECT tblEmployees.ID, tblEmployees.Last_Name, tblEmployees.First_Name, tblEmployees.SS_ID INTO Test" _
& "FROM tblEmployees" _
& "WHERE (((tblEmployees.ID)=[What number]));"
Set qdf = CurrentDb.QueryDefs("Query1")
qdf.Parameters(0).Value = InputBox("What is the ID Number")
qdf.Execute
qdf.Close
Set db = Nothing
Set qdf = Nothing
End Sub
So to apply this to the 15 queries I would just Dim other variables as DAO.QueryDef right? I'm note really sure i need the SQLString part either? Also, I noticed that when running this mock code it took quite a while for it to create the new table. Is this normal? Also also, the code will not run if the table it is creating already exists. Is there a way to just have the code replace the existing table with the new one? Kind of new to this VBA so thanks for your patience.
Short answer is yes, this is possible. Some keywords you want to familiarize yourself with are "parameters" which are the variables with the prompt and "Querydef" or query definition.
There are quite a few articles detailing how to pass parameter values to a query programmatically. Check out this page for a solid overview of how to accomplish this. Most notably, the last example uses an inputbox to prompt the user to provide the parameter value, which gets you close to what you need. (ie. cmd.Parameters(0).Value = InputBox("Enter a country name"))
Modified to your design, it might be best to create a string variable and ask for the parameter first, then use the variable in declaring the parameters individually, which would permit a single parameter submission that gets applied to all queries.
EDIT
I have adjusted your code to show you how to go about it. You will have to repeat the block for each query.
Private Sub btnTest_Click()
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim strParamter As String
Set db = CurrentDb
strParamter = InputBox("What is the ID Number")
Set qdf = db.QueryDefs("Query1")
qdf.Parameters(0).Value = strParameter
qdf.Execute
qdf.Close
'Now move to next query
Set qdf = db.QueryDefs("Query2")
qdf.Parameters(0).Value = strParameter
qdf.Execute
qdf.Close
'...etc.
Set qdf = Nothing
Set db = Nothing
End Sub

MSAccess: Get ItemIDs from Recordsetclone into New Table?

I have an AccessDB app where I need to grab the ItemIDs for the current user-applied filter into a new table to use downstream. Using the subform datasheet .recordsetclone property I can see the desired recordset, .recordcount reports the correct number of records. Otherwise, the following does not produce the desired temp table and AccessVBA does not complain.
Dim db As DAO.Database
Dim rstItemIDs As DAO.Recordset
Dim strSQL as String
Set db = CurrentDb
set rstItemIDs = Forms!Mainform![Data subform].Form.RecordsetClone
msgbox rstItemIDs.recordcount 'reports the correct result
strSQL = "SELECT rstItemIDs.ItemID INTO tempTable FROM rstItemIDs;"
db.Execute strSQL
Is it possible to construct a SQL Select query against a dao.recordset?
Thanks for any pointers you can provide.
Access SQL will not accept either a DAO or ADODB Recordset as the data source for a query.
However, I'm puzzled that Access does not complain when you try. With every attempt I made to reproduce your sample code, I got error #3078, "The Microsoft Access database engine cannot find the input table or query 'rstItemIDs'. Make sure it exists and that its name is spelled correctly."
Even DoCmd.SetWarnings False did not suppress that error message.
If you're interested in alternatives, you could persist tempTable (instead of creating a new version each time), then delete its contents and move through rstItemIDs adding each value to the second recordset. Although that is a RBAR (row by agonizing row) method, it may not be too painful with a small recordset.
A set-based approach could be to create a query based on your form's .RecordSource and .Filter properties. For example, with my form's .RecordSource as SELECT * FROM foo and the current form .Filter as id>10, this would give me a SELECT which returns the form's filtered records:
Replace(Me.RecordSource, ";", "") & vbcrlf & "AND " & Me.Filter

Pass one line of recordset to a function

While I am moving through a recordset, I want to pass the current line through to another function. How could I do that?
I have set rs = "my_query". As I loop through rs, starting with the first record and moving through until the last record, I pass the current record to another function that fills out a table with all of the fields in the query. Right now I have to list every field I want passed into the other function and written to the table. It seems like there should be an easier way to get the current record written to a table. In the example below I am only showing 3 fields. "my_query" actually has a lot of fields. It is also a lot of work to change all of the references to the WritetoTable function when we add or remove fields from the query.
I'd like to just pass the whole rs to the WritetoTable function, but I don't know how to do that while making sure I only write the one record I want into the table.
Set rs = "my_query"
rs.MoveFirst
Do While Not rs.EOF
Call WritetoTable(rs!field1, rs!field2, rs!field3......)
rs.MoveNext
Loop
Function WritetoTable(field1 as string, field2 as string, field3 as string...)
Dim rsTable as DAO.Recordset
Set rsTable = CurrentDb.OpenRecordset(Table,dbOpenDynaset)
With rsTable
.AddNew
!Field1 = field1
!Field2 = field2
!Field3 = field3
.update
End With
rsTable.Close
Set rsTable = Nothing
End Function
Thank you to Remou and Overmind for leading me in the right direction. I decided to use the bookmark property of the recordset to ensure I could come back to the same place. The code below looks at each line in the query result and passes it to the WritetoTable function.
It is true that I could simplify this to an append query if it was as simple as the code I have shown. In my situation it would take a lot of time to run such specific queries off a big server table. So I run one query that has data in it that needs to be sorted out into various tables. The query still takes a long time but at least it only has to run once. I then have to go through the query results one line at a time to see which table it should be written to.
The code below lets me look at each line of the query result. When it needs to be written to my table I can pass the whole recordset into the WritetoTable function and use the bookmark to write only the one line I was looking at. I don't know if the code runs slower or faster than what I had before, but it is easier to edit and make changes to.
Set rs = "my_query"
rs.MoveFirst
Do While Not rs.EOF
vPosition = rs.Bookmark
Call WritetoTable(rs, vPosition)
rs.MoveNext
Loop
Function WritetoTable(rs as Recordset, vPosition as Variant)
Dim rsTable as DAO.Recordset
Set rsTable = CurrentDb.OpenRecordset(Table,dbOpenDynaset)
rs.Bookmark = vPosition
With rsTable
.AddNew
!Field1 = rs!field1
!Field2 = rs!field2
!Field3 = rs!field3
.update
End With
rsTable.Close
Set rsTable = Nothing
End Function
An append query would be much simpler and much faster. However, you have to ask yourself, do I really need the same data in two tables?
Dim db As Database
Set db = CurrentDB
sSQL = "INSERT INTO ATable (Field1, Field2) " _
& "SELECT FieldA, FieldB FROM BTable " _
& "WHERE BTable FieldX='Y'"
db.Execute sSQL, dbFailOnError
You could also have a saved query and simply run it in VBA. For Example:
db.Execute "AQuery", dbFailOnError
Note that db.Execute will only work with action queries.
It is often best to use an instance of CurrentDb, because it will allow you to get RecordsAffected.
You can also append from an external database, for example:
INSERT INTO ATable SELECT * FROM [ODBC;DRIVER=SQL Server Native Client 11.0;SERVER=Server;DATABASE=Database;uid=User;pwd=Password].AnotherTable t WHERE t.FieldX Like "w*"
You need to ensure you have a good connection string. It is generally best to list the fields / columns, rather than trying a wildcard.

Get ID of Last Inserted Record - Access DAO, ODBC, SQL Server 2008 Identity Field

It's a very common question but I'm having trouble getting the ID of the last inserted record. I'm using DAO with ODBC linked tables to duplicate a record and it's child records. My tables are in SQL Server 2008 and have Identity fields for ID fields.
Here's what I've tried so far. My first bit of code here results in error 3167, Record is Deleted. If I do a debug.Print the recordset actually contains 3 records.
Dim r as DAO.Recordset, db as DAO.Database
Set db = CurrentDb
Set r = db.OpenRecordset("SELECT TOP 2 * FROM item ORDER BY DateTimeModified DESC", dbOpenDynaset, dbSeeChanges)
r.AddNew
'Set field values here
r.Update 'Completes without error
r.Bookmark = r.LastModified
Debug.Print r("ItemID") 'Error 3167, Record is deleted
Here's the next thing I tried:
Debug.Print db.OpenRecordset("SELECT ##identity FROM item")(0)
This last one completes without any problem but the value returned is incorrect. Where the actual new ItemID is 321 this returns the value 614. The value it is returning does appear to be incremental (it changes as I keep testing this) but it does not appear to relate at all to my table. There is no field with the value 614. I've double checked to make sure I'm looking up the correct table.
I know I could use something like DLookup or DMax but I don't think that would be considered bullet proof in a multi-user environment.
I suppose I could use a Stored Procedure with ADO to get around this problem. I'm wondering if that is my only option?
Edit1:
I'm now using the following code and it is doing what I need/want it to. I suspect this is basically the same as using DMax.
Dim r as DAO.Recordset, db as DAO.Database
Set db = CurrentDb
Set r = db.OpenRecordset("SELECT TOP 1 * FROM item ORDER BY ItemID DESC", dbOpenDynaset, dbSeeChanges)
r.AddNew
'Set field values here
r.Update
r.Requery
r.MoveFirst
Debug.Print r("ItemID")
As far as I'm aware ##IDENTITY doesn't work for cursor-based inserts. DAO and ADO both use cursors behind the scenes.
After you .Update the record you should be able to get the identity value back simply by reading the value.
The following works fine for me via an ADO Recordset opened with Keyset semantics:
r.Update
Debug.Print r("ItemID")
The following works fine for me via a DAO Recordset opened with Dynaset semantics:
r.Update
r.Bookmark = r.LastModified
Debug.Print r("ItemID")
You should avoid .Requery and .MoveFirst, you're introducing concurrency problems. Consider:
Dim r as DAO.Recordset, db as DAO.Database
Set db = CurrentDb
Set r = db.OpenRecordset("SELECT TOP 1 * FROM item ORDER BY ItemID DESC", dbOpenDynaset, dbSeeChanges)
r.AddNew
''// Set field values here
r.Update
''// At this point another user adds a new record
r.Requery
r.MoveFirst ''// ORDER BY ItemID DESC means that you're going to see the new user's row
Debug.Print r("ItemID")
The following works as expected (using Office 2013 and SQL Server 2014)
Set rsProjects = db.OpenRecordset("JobProjects", dbOpenDynaset, dbSeeChanges Or dbAppendOnly)
rsProjects.AddNew
rsProjects.Name = 'xyz'
rsProjects.Update
rsProjects.Bookmark = rsProjects.LastModified
lNewProjectID = rsProjects!ProjectID.Value
Key point: instead of using 'SELECT TOP 2' or 'SELECT TOP 1', etc. I used 'dbSeeChanges Or dbAppendOnly'. I verified in sql profiler that opening the recordset does not generate any queries to SQL Server.
When you issue the update, access generates an insert statement followed immediately by a SELECT ##IDENTITY to get the id of the new record.
Edited: add missing .AddNew, Remove duplicate .Update.
it dosnt work with sql server backend(in multi user app
). for access table it work
for sql use stored procedure.use this way
CREATE PROCEDURE dbo.AddAsset
#Name VARCHAR(500),
#URL VARCHAR(2000),
#new_identity INT = NULL OUTPUT
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.Assets(Name, URL) SELECT #Name, #URL;
SET #new_identity = SCOPE_IDENTITY();
END
GO
then use this sp in front end
Simply get the value of the key field before you execute the Update statement. As pointed out in the comment below, this process will not work if you are using a different backend than Microsoft Access. But I leave this response here in case that is your use case and you are just searching for an answer to the general question of how you get the ID of the last inserted record.
For your example, you can do this with Microsoft Access:
Dim r as DAO.Recordset, db as DAO.Database
Dim lKey As Long
Set db = CurrentDb
Set r = db.OpenRecordset("SELECT TOP 2 * FROM item ORDER BY DateTimeModified DESC", dbOpenDynaset, dbSeeChanges)
r.AddNew
'Set field values here
'Retrieve the key value before executing the Update
lKey = r!ItemID
r.Update
Debug.Print lKey

Access: How to execute a query and save its result in a report

HI,
I am trying to write a query in vba and to save its result in a report.
I am a beginner. this is what i have tried
can somebody correct me
Dim cn As New ADODB.Connection, rs As New ADODB.Recordset
Dim sql As String
Set cn = CurrentProject.Connection
sql = "Select * from table1 where empno is 0"
rs.Open sql, cn
While Not rs.EOF
' here i think i should save the result in a report but i am not sure how
rs.MoveNext
Wend
rs.Close
cn.Close
Set rs = Nothing
Set cn = Nothing
Also how do i change this query to run this on all tables in a database
IF you are wanting to create a report using MS Access's report generator, you will have to use a Query Object (there might be a way to trick MS Access into running it off of your record set, but it's probably not worth your effort).
You can create the Query Object on the "Database" window. Click the Query button in the objects list, and then click on New. In the resulting editor you can create the query graphically or if you prefer with SQL. Save the query and give it a meaning full name.
Similarly the report can be created on the "Database" window. Click on the Report button and then on New. In the resulting wizard, you'll link the report to the query you just created.
Update: As D.W. Fenton said, you can embed the query right within the Report Object without creating a separate Query Object. My preference is to create one anyway.
The problem with this method is you would have to create a separate query and report for each table.
IF you just want to dump the result out to a text file (to read/print later), then you can do it using recordsets like you are in your VBA code. It will look something like this
'...
dim strFoo as string
dim strBar as string
'...
if not rs.bof then
rd.MoveFirst
end if
While Not rs.EOF
strFoo = rs("foo") 'copy the value in the field
'named "foo" into strFoo.
strBar = rs("bar")
'... etc. for all fields you want
'
'write out the values to a text file
'(I'll leave this an exercise for the reader)
'
rs.MoveNext
Wend
'...
Parsing all of the tables can be done in a loop something like this:
dim strTableName as string
dim db As Database
'...
Set db = CurrentDb
db.TableDefs.Refresh
For Each myTable In db.TableDefs
If Len(myTable.Connect) > 0 Then
strTableName = myTable.Name
'...
'Do something with the table
'...
End If
Next
set db = nothing
=======================UPDATE=======================
It is possible to run an MS-Access Report from a record set. To repease what I said to tksy's question
From Access Web you can use the "name" property of a recordset. You resulting code would look something like this:
In the report
Private Sub Report_Open(Cancel As Integer)
Me.RecordSource = gMyRecordSet.Name
End Sub
In the calling object (module, form, etc.)
Public gMyRecordSet As Recordset
'...
Public Sub callMyReport()
'...
Set gMyRecordSet = CurrentDb.OpenRecordset("Select * " & _
"from foo " & _
"where bar='yaddah'")
DoCmd.OpenReport "myReport", acViewPreview
'...
gMyRecordSet.Close
Set gMyRecordSet = Nothing
'...
End Sub
Q.E.D.
Normally you would design the report based on a data source. Then after your report is done and working properly you use VBA to display or save the report.
To run this for each table in the database, I'd suggest writing a function that looped through CurrentData.AllTables(i) and then called your function above in each iteration
Hope this helps
If you want to simply view the results, you can create a query. For example, here is some rough, mostly untested VBA:
Sub ViewMySQL
Dim strSQL as String
Dim strName As String
'Note that this is not sensible in that you
'will end up with as many queries open as there are tables
For Each tdf In CurrentDB.TableDefs
If Left(tdf.Name,4)<>"Msys" Then
strName = "tmp" & tdf.Name
strSQL = "Select * from [" & tdf.Name & "] where empno = 0"
UpdateQuery strName, strSQL
DoCmd.OpenQuery strName, acViewNormal
End If
Next
End Sub
Function UpdateQuery(QueryName, SQL)
If IsNull(DLookup("Name", "MsysObjects", "Name='" & QueryName & "'")) Then
CurrentDb.CreateQueryDef QueryName, SQL
Else
CurrentDb.QueryDefs(QueryName).SQL = SQL
End If
UpdateQuery = True
End Function
You may also be interested in MDB Doc, an add-in for Microsoft Access 97-2003 that allows you to document objects/properties within an Access database to an HTML file.
-- http://mdbdoc.sourceforge.net/
It's not entirely clear to me what you want to do. If you want to view the results of SQL statement, you'd create a form and set its recordsource to "Select * from table1 where empno is 0". Then you could view the results one record at a time.
If that's not what you want, then I'm afraid I just don't have enough information to answer your question.
From what you have said so far, I don't see any reason why you need VBA or a report, since you just want to view the data. A report is for printing, a form is for viewing and editing. A report is page-oriented and not that easy to navigate, while a form is record-oriented, and allows you to edit the data (if you want to).
More information about what you want to accomplish will help us give you better answers.
Had the same question. just use the clipboard!
select the query results by click/dragging over all field names shown
press ctrl-c to copy to windows clipboard
open a blank document in word and click inside it
press ctrl-v to paste from clipboard.