My database has three tables, Holder, Product (which is an account) and Transaction. I have set up a form from the Holder table that has a subform from the Product table and the Product subform has the Transaction table as a subform. On the Holder portion of the form, I have put an Outstanding unbound text field that should display the total amount and tax fields of transactions from the transaction table that have not been paid (indicated by a checkbox on the Transaction table). I have set the control source of the unbound text box to =calcOutstanding() and written the following function for the form.
Public Function calcOutstanding()
Dim db As Database
Dim strSQL As String
Set db = CurrentDb
strSQL = "SELECT SUM(tblTransaction.TxAmount + tblTransaction.TxTax) As Outstanding" _
& "FROM tblTransaction" _
& "INNER JOIN tblProduct ON tblTransaction.fkProductID = tblProduct.ProductID" _
& "INNER JOIN tblHolder ON tblProduct.fkHolderID = tblHolder.HolderID" _
& "WHERE tblTransaction.TxPaid = False" _
& "AND tlbHolder.HolderID = Me.HolderID;"
DoCmd.RunSQL strSQL
calcOutstanding = Outstanding
End Function
The field now just shows #Error. What am I doing wrong?
There's a bunch wrong with your approach:
DoCmd.RunSQL is just for action queries (INSERT, UPDATE, DELETE).
You cannot return a value from DoCmd.RunSQL like you are attempting to and push it into a variable.
Your concatenation for the where clause is incorrect.
As HansUp mentioned, Access is very picky about parentheses in JOINs in its SQL.
Assuming the SQL is correct, this code lives on the parent form, and you dno't get multiple rows back in your query, maybe something like this would work:
Public Function calcOutstanding() As Currency
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim strSQL As String
Set db = CurrentDb
strSQL = "SELECT SUM(tblTransaction.TxAmount + tblTransaction.TxTax) As Outstanding " _
& "FROM (tblTransaction " _
& "INNER JOIN tblProduct ON tblTransaction.fkProductID = tblProduct.ProductID) " _
& "INNER JOIN tblHolder ON tblProduct.fkHolderID = tblHolder.HolderID " _
& "WHERE tblTransaction.TxPaid = False " _
& "AND tlbHolder.HolderID = " & Me.HolderID
Set rst = db.OpenRecordset(strSQL, dbForwardOnly)
calcOutstanding = rst![Outstanding]
Set rst = Nothing
set db = Nothing
End Function
Notice the concatenation in the WHERE clause to get the value from the form's data source (otherwise the SQL couldn't reconcile Me.HolderID within the scope of the SQL itself). Also, we push the returning dataset into a recordset and read from that. Something along these lines should work, I think. (Not in front of Access now, so sorry if any non-compiling statements.)
EDIT: Added the function return type as integer for specificity's sake.
EDIT 2: Added the function return type as currencyfor specificity's sake. Doh.
Right off the bat, I see a problem in the code you posted:
strSQL = "SELECT SUM(tblTransaction.TxAmount + tblTransaction.TxTax) As Outstanding" _
& "FROM tblTransaction" _
& "INNER JOIN tblProduct ON tblTransaction.fkProductID = tblProduct.ProductID" _
& "INNER JOIN tblHolder ON tblProduct.fkHolderID = tblHolder.HolderID" _
& "WHERE tblTransaction.TxPaid = False" _
& "AND tlbHolder.HolderID = Me.HolderID;"
There should be a space either at the end of each line Outstanding " _ or at the beginning of each line like " FROM tblTransaction otherwise your string will read OutstandingFROM tblTransaction when parsed, which will give you an error.
I doubt you need an external function to do this. MS Access allows you to reference fields from subform simply by Me.Subform!FieldName.Value
This means, you could simply access the subform fields that are related to your current record. Even perform IIF(condition, truevalue, falsevalue) on that field
read more about accessing forms and subforms here: http://access.mvps.org/access/forms/frm0031.htm
EDIT:
in your third subform (tbl_transaction), create a new unbound TextBox called (txt_outstanding) and assign this expression
=IIF([txPaid]=false, sum(txAMount +TxTax),0)
now you can access this field in your parent form something similar to this:
me.txt_someTextbox.value = nz(me.tbltransactionsubform!txt_outstanding.value,"")
Related
I know how to do this, but wondering if I might be able to write a more elegant solution. I have a form with a tab control. The control has 14 pages, each one, with it's own sub form. One of the pages (pgRequirements) has a subform of requirements, with a combo control "Requirement Type". It is a continuous form, so the user can add as many requirements as they want, for the main record.
There are 9 of those requirements, which have their own tab control page / sub form. I want to set visibility of those tab control pages, based on this parent's sub form requirements. So a current main record, can have multiple sub-requirement records. If any of those match e.g. requirement type A, than page A should be visible, otherwise it should not be.
I need this code to run anytime the main form is loaded, and the detail is made visible (meaning a main record has been chosen from a find form). Also anytime a requirement record is added or removed. The below is assuming that the parent-child links on the main form to subform will limit the requirement records, to just those that are for the current main record.
Here is the simple code, that will do the job, but is probably over-written:
If Me.FKRequirementType.Column(1) = "ReqType1" Then
Me.Parent!pgReqType1.Visible = True
Else
Me.Parent!pgReqType1.Visible = False
End If
If Me.FKRequirementType.Column(1) = "ReqType2" Then
Me.Parent!pgReqType2.Visible = True
Else
Me.Parent!pgReqType2.Visible = False
End If
If Me.FKRequirementType.Column(1) = "ReqType3" Then
Me.Parent!pgReqType3.Visible = True
Else
Me.Parent!pgReqType3.Visible = False
End If
If Me.FKRequirementType.Column(1) = "ReqType4" Then
Me.Parent!pgReqType4.Visible = True
Else
Me.Parent!pgReqType4.Visible = False
End If
Thanks!
EDIT
I turned this into a public function, so I can call it from anywhere. One problem. It's not working lol (small problem). I don't get any errors, but all the tab control pages are visible. When I add a new record, most of them should be hidden. I have a tblReqType table, with all the requirement types. I added a column to this, with the exact name of it's corresponding tab control page name, so I can loop through that table, for all records where that page name is not null, and set their page visible or not, based on the current main record ID having a record-requirement (cross reference table) record for each requirement type.
This is the public function I wrote. Can anyone help me understand what I'm missing in these loops for setting the visibility true (vtrue) vs setting the visibility false (vfalse)
Public Function ShowRequirements()
Dim db As DAO.Database
Dim strRstVTrue As String
Dim rstvTrue As DAO.Recordset
Dim strRstVFalse As String
Dim rstvFalse As DAO.Recordset
Dim strFieldName As String
'Setup the recordset
Set db = CurrentDb
strRstVTrue = "SELECT tblMRecordRequirements.ID, tblMRecordRequirements.FKMC, tblReqType.txtRequirementPage " & _
"FROM tblMRecordRequirements LEFT JOIN tblReqType ON tblMRecordRequirements.FKRequirementType = tblReqType.ID " & _
"WHERE tblReqType.txtRequirementPage Is Not Null AND tblMRecordRequirements.FKMC = " & Nz(Forms!frmMRecords!ID, 0)
strRstVFalse = "SELECT tblReqType.ID, tblReqType.txtRequirementPage, tblMRecordRequirements.FKMC " & _
"FROM tblReqType LEFT JOIN tblMRecordRequirements ON tblReqType.ID = tblMRecordRequirements.FKRequirementType " & _
"WHERE tblReqType.txtRequirementPage Is Not Null AND tblMRecordRequirements.FKMC <> " & Nz(Forms!frmMRecords!ID, 0)
Set rstvTrue = CurrentDb.OpenRecordset(strRstVTrue, dbOpenDynaset, dbSeeChanges)
Set rstvFalse = CurrentDb.OpenRecordset(strRstVFalse, dbOpenDynaset, dbSeeChanges)
strFieldName = "txtRequirementPage"
Do While Not rstvTrue.EOF
Forms!frmMRecords.tbMRecordSubs.Pages(rstvTrue.Fields(strFieldName)).Visible = True
Loop
Do While Not rstvFalse.EOF
Forms!frmMRecords.tbMRecordSubs.Pages(rstvFalse.Fields(strFieldName)).Visible = False
Loop
End Function
If anyone can help me figure out my stupidity, you deserve an up vote, a check mark, and a cookie.
EDIT again
Below is updated code for the public function. I fixed the rs for the true query, and I added in the MoveNext for the loops.
Public Function ShowRequirements()
Dim db As DAO.Database
Dim strRstVTrue As String
Dim rstvTrue As DAO.Recordset
Dim strRstVFalse As String
Dim rstvFalse As DAO.Recordset
Dim strFieldName As String
'Setup the recordset
Set db = CurrentDb
strRstVTrue = "SELECT tblMRecordRequirements.ID, tblMRecordRequirements.FKMC, tblReqType.txtRequirementPage " & _
"FROM tblMRecordRequirements LEFT JOIN tblReqType ON tblMRecordRequirements.FKRequirementType = tblReqType.ID " & _
"WHERE tblReqType.txtRequirementPage Is Not Null AND tblMRecordRequirements.FKMC = " & Nz(Forms!frmMRecords!ID, 0)
strRstVFalse = "SELECT tblReqType.ID, tblReqType.txtRequirementPage, tblMRecordRequirements.FKMC " & _
"FROM tblReqType LEFT JOIN tblMRecordRequirements ON tblReqType.ID = tblMRecordRequirements.FKRequirementType " & _
"WHERE tblReqType.txtRequirementPage Is Not Null AND tblMRecordRequirements.FKMC <> Is Null"
Set rstvTrue = CurrentDb.OpenRecordset(strRstVTrue, dbOpenDynaset, dbSeeChanges)
Set rstvFalse = CurrentDb.OpenRecordset(strRstVFalse, dbOpenDynaset, dbSeeChanges)
strFieldName = "txtRequirementPage"
Do While Not rstvTrue.EOF
Forms!frmMRecords.tbMRecordSubs.Pages(rstvTrue.Fields(strFieldName)).Visible = True
rstvTrue.MoveNext
Loop
Do While Not rstvFalse.EOF
Forms!frmMRecords.tbMRecordSubs.Pages(rstvFalse.Fields(strFieldName)).Visible = False
rstvFalse.MoveNext
Loop
End Function
EDIT REDUX
I think I may have it worked out, but let me know what you all think. I really appreciate all your thoughts on this, as I know you all have a lot of experience not just in figuring out these kinds of challenges, but ensuring the code is good and not prone to issues.
Here is where I am at:
Public Function ShowRequirements()
Dim db As DAO.Database
Dim db2 As DAO.Database
Dim strRstVTrue As String
Dim rstvTrue As DAO.Recordset
Dim strRstVFalse As String
Dim rstvFalse As DAO.Recordset
Dim strFieldName As String
strFieldName = "txtRequirementPage"
Set db = CurrentDb
Set db2 = CurrentDb
strRstVTrue = "SELECT tblReqType.txtRequirementPage " & _
"FROM tblReqType LEFT JOIN tblMRecordRequirements ON tblMRecordRequirements.FKRequirementType = tblReqType.ID " & _
"WHERE tblReqType.txtRequirementPage Is Not Null AND tblMRecordRequirements.FKMC = " & MCID
strRstVFalse = "SELECT tblReqType.txtRequirementPage " & _
"FROM tblReqType LEFT JOIN tblMRecordRequirements ON tblMRecordRequirements.FKRequirementType = tblReqType.ID " & _
"WHERE tblMRecordRequirements.ID Not In (Select ID From [tblMRecordRequirements] WHERE [tblMRecordRequirements]![FKMC] = " & MCID & _
") AND tblReqType.txtRequirementPage Is Not Null;"
Set rstvTrue = db.OpenRecordset(strRstVTrue, dbOpenDynaset, dbSeeChanges)
Set rstvFalse = db2.OpenRecordset(strRstVFalse, dbOpenDynaset, dbSeeChanges)
Do While Not rstvTrue.EOF
Forms!frmMRecords.tbMRecordSubs.Pages(rstvTrue.Fields(strFieldName)).Visible = True
rstvTrue.MoveNext
Loop
Do While Not rstvFalse.EOF
Forms!frmMRecords.tbMRecordSubs.Pages(rstvFalse.Fields(strFieldName)).Visible = False
rstvFalse.MoveNext
Loop
End Function
I need this code to run anytime the main form is loaded, and the detail is made visible (meaning a main record has been chosen from a find form). Also anytime a requirement record is added.
Put the code you shared inside a sub procedure and call the sub procedure from Form_Load(), Form_Current(), Form_AfterInsert() event handler, etc.
As for elegance, I'd focus on maintainability and efficiency rather than looks, but concise code is also nice. 1) You can use a With block to reduce redundant object method calls, but that'll only work for one reference at a time. 2) Instead create another variable to temporarily hold a value/object from a series of dot property-accessors. 3) It looks like pages and column values are already numbered with a consistent naming pattern, so leverage that in a loop. 4) Comparison operations in VBA are largely Boolean operations, so they return True or False. The result of an entire Boolean expression can be assigned to another Boolean variable/property. (Boolean operations can also return Null... which is usually, but not always, treated like False. If you're certain that your data doesn't have Null values, then you can simplify the code and ignore this issue. If the data can contain null, then you need to adjust the code appropriately.)
Me.Parent!pgReqType1 is calling the default property of the Parent form which is Controls, which default property is Item. The bang operator ! passes the code text as a string into the collection Item method. In short, it is equivalent to Me.Parent.Controls.Item("pgReqType1").
Dim i as integer
Dim ctls as Controls
Dim reqValue as string
Set ctls = Me.Parent.Controls
reqValue = Me.FKRequirementType.Column(1)
For i = 1 to 4
ctls.Item("pgReqType" & i).Visible = (reqValue = "ReqType" & i)
Next i
About all I can do is translate the specific code snippet you show. I have the feeling there is probably more to it than this, because the code snippet you shared ensures that there will only be one tab visible: It is testing the same column value multiple times which could only have one value. Bug? Incomplete example?
This really goes against my better judgement of Stack Overflow principles --to not answer multi-part, continuing debugging questions-- but I really want a cookie.
How could the posted code have ever worked, since you are not moving through either recordset? There are no MoveNext calls. That means that both recordsets are empty or an error is being thrown that is being ignored somewhere (i.e. On Error Resume Next). Otherwise, it should lock up Access with infinite loops. Sometimes you can stop the code with Ctrl+Break, but not always successful in Access.
More precise table schema is required to properly interpret your data, but I'll make some assumptions. You state that tblReqType contains all requirement types. I'll assume that tblMRecordRequirements contains rows only for requirements which are "applied" (a.k.a. "on", "selected") for the ID value in tblMRecordRequirements.FKMC. Assuming the converse, if there are no rows in tblMRecordRequirements with ID in tblMRecordRequirements.FKMC for a given tblMRecordRequirements.FKRequirementType, then the requirement is not "applied" to that ID.
Does every row in tblReqType have a value in txtRequirementPage, or do some rows have null values? Also, can multiple requirements have the same page specified? Or is it a true one-to-one requirement-to-page mapping with no null values?
First of all why would the first query not be an INNER JOIN, since I assume that only records that match in both tables should be returned for the Visible = True condition? Depending on your answers above, this would probably make the condition tblReqType.txtRequirementPage Is Not Null unnecessary in the first query.
Simply reversing a LEFT JOIN will not return what you want especially if you select all other ID values ( tblMRecordRequirements.FKMC <> Nz(Forms!frmMRecords!ID, 0) ). All that does is give you the requirements for every other ID values. Not only will that be inefficient since it could return many, many irrelevant records, it could be likely that over all other ID values that every possible requirement would be applied, so that the second query will essentially cause all requirements to be invisible.
Further picky observations:
If Forms!frmMRecords!ID is null, then you might as well not even execute the queries. You should check that value for null separately and perform appropriate actions rather than letting that specific condition fall through the other code, even if the end side effect is what you desire. It makes the code harder to debug and interpret properly. In other words write code that does "If ID is null, set all pages to visible = false, then exit the sub (i.e. skip other code)"
It's more efficient to get a read-only snapshot rather than an fully-updatable Dynaset recordset: Too much overhead just for looping through without data manipulation.
Proper break points, debug output and error handling code can help identify bad code. It is worth tracing through results of recordsets manually to inspect values and proper SQL syntax.
Try this:
Public Sub ShowRequirements()
Dim db As DAO.Database
Dim iID As Long '? Assuming long integer
Dim strSQL As String
Dim rsTabs As DAO.Recordset
On Error Resume Next
iID = -1 '* Set to bogus value
If Not IsNull(Forms!frmMRecords!ID) Then
iID = Forms!frmMRecords!ID
End If
If iID = -1 Or Err.Number <> 0 Then
'* Problem accessing ID control on form (empty recordset, new record row, etc.)
'* or it is null
'Set all tab pages to Visible = False?
Exit Sub
End If
On Error GoTo Catch
'* Setup the recordset
Set db = CurrentDb
'* Use embedded query (replacable with saved query) for filtering on ID values.
'* This is critical so that the LEFT JOIN does not return or filter records
'* based on other ID values.
strSQL = _
"SELECT tblReqType.ID, tblReqType.txtRequirementPage, (IDReq.FKRequirementType Is Not Null) As ShowTab " & _
" FROM tblReqType LEFT JOIN" & _
" (SELECT MReq.FKRequirementType FROM tblMRecordRequirements AS MReq " & _
" WHERE MReq.FKMC = " & iID & ") AS IDReq" & _
" ON tblReqType.ID = IDReq.FKRequirementType" & _
" WHERE tblReqType.txtRequirementPage Is Not Null"
Set rsTabs = db.OpenRecordset(strRstVTrue, dbOpenSnapshot, dbReadOnly)
Do While Not rsTabs.EOF
Forms!frmMRecords.tbMRecordSubs.Pages(rsTabs!txtRequirementPage).Visible = rsTabs!ShowTab
rsTabs.MoveNext '* Advance recordset. (Avoid infinite loops.)
Loop
CloseAll:
On Error Resume Next
'* Best habit to explicitly close recordsets and database connections, even when not necessary (since they'll close automatically when they go out of scope)
If Not rsTabs Is Nothing Then
rsTabs.Close
Set rsTabs = Nothing
End If
Set db = Nothing
Exit Sub
Catch:
'* At least report error for development
Debug.Print "ShowRequirements(): Error: " & Err.Number & ": " & Err.Description
'* Show MsgBox or update form status control?
'* Set all tab pages to Visible = False?
'* Form state could be misleading without proper user notification and/or error handling
Resume CloseAll
End Sub
I'm trying to get an access userform button to run an update query.
The parameters I have are:
Invoice number (should update Current ICB.Invoice number)
Material Code (should update Current ICB.Material Code)
Amount USD (Should update current ICB.Amount USD)
Username (Should update current ICB.Owner)
Vendor Code (Should update current ICB.Vendor code)
Record Number (Should = current ICB.ID)
I want to use a button on the form to check for a record that has the same record number and update the fields listed above with the values entered into the userform. I would use an update query but writing SQL into VBA is not my strong point. Any ideas?
In the OnClick event of the button, do something like this:
Dim db as Database
Dim rec as Recordset
Set db = CurrentDb
Set rec = db.OpenRecordset("Select * from MyTable where RecordNumber = " & ICB.ID & "")
rec.Edit
rec("InvoiceNumber") = ICB.Invoice_number
rec("MaterialCode") = ICB.Material_Code
etc...
rec.update
rec.close
Use your actual table and field names, but that's the general idea.
If you absolutely insist on using a query, then you need to write the SQL in code in a similar way and use DoCmd.RunSQL. Something like this:
txtSQL = "UPDATE MyTable SET InvoiceNumber = " & ICB.Invoice_number & ", " & _
" MaterialCode = " & ICB.Material_Code & "" & _
etc...
" WHERE RecordNumber = " & ICB.ID & ""
DoCmd.RunSQL txtSQL
This assumes you're only using integers. If you're storing the data as text, it needs to be surrounded by single quotes.
" MaterialCode = '" & ICB.Material_Code & "'" & _
I have a database containing locations of water wells and a ton of properties associated with those wells. All tables are linked with WELL_ID (name of the well), which is "short text" data type, from what I can tell. Within the database there are existing queries from which I'm trying to get the data (I don't want to mess with the tables in case I make a mistake and mess something up).
I've created a form where the user inputs UTM coordinates for easting and northing, as well as a search radius, then clicks a "search" button. Upon clicking search, the procedure creates a recordset of the [qryUTM_NAD83], then calculates the radial distance of each well and if it is within the specified search radius, it is stored in a new [Search_Results] table using INSERT INTO.
Now, once the well is identified as meeting search criteria the WELL_ID is stored, and passed to a function which searches through a recordset of a different query [qryFormation]. This query has a one-to-many relationship where there is a record for each geologic layer, each having the same WELL_ID (i.e. each well has multiple layers but all these layers have the same WELL_ID). I need to concatenate these layers into one string, pass them back to the search function and add it to the [Search_Results] table. However, i get a data type mismatch error in the SQL statement.
Here's the code I have:
(I've omitted some parts of code to keep it short for you all)
Private Sub SearchButton_Click()
Dim WellID As String, mySQL As String, NewLitho As String
'Creating new recordsets from [qryUTM_NAD83] table
Dim rs1 As DAO.Recordset
'[ZONE] is just user specified, helps me narrow the search a little
Set rs1 = CurrentDb.OpenRecordset("SELECT [qryUTM_NAD83].* " & _
"FROM [qryUTM_NAD83] " & _
"WHERE [ZONE] = " & frmZone, dbOpenDynaset)
'Moving through the recordset
rs1.MoveFirst
Do While Not rs1.EOF
'calculated radius, r , for this well (omitted)
If r < SearchRadius Then
WellID = Val(rs1.Fields("WELL_ID").Value)
NewLitho = LithoTransform(WellID)
mySQL = "INSERT INTO [Search_Results] " & _
"([WELL_ID], [Easting], [Northing], [Radius], [Lithology]) " & _
"VALUES (" & WellID & ", " & x & ", " & y & ", " & r & ", " & NewLitho & ")"
CurrentDb.Execute mySQL
rs1.MoveNext
End If
Loop
End Sub
The function:
(error occurs in "Set rs2..." - data type mismatch)
Public Function LithoTransform(CurrentID As String) As String
'CurrentID is the well which was identified as being within the search radius
Dim rs2 As DAO.Recordset
Dim mySQL2 As String
'SQL statement and new recordset of the well we are looking at in the search
Debug.Print CurrentID
mySQL2 = "SELECT [qryFormation].* " & _
"FROM [qryFormation] " & _
"WHERE [WELL_ID] = " & CurrentID
Set rs2 = CurrentDb.OpenRecordset(mySQL2, dbOpenDynaset)
'Move through recordset rs2 and concatenating into new string
'Bunch of code here
'Close recordset set it to 'nothing'
End Function
Since WELL_ID is text type, include quotes around the value of CurrentID when you include it in your WHERE clause. So the quickest fix is probably this ...
mySQL2 = "SELECT [qryFormation].* " & _
"FROM [qryFormation] " & _
"WHERE [WELL_ID] = '" & CurrentID & "'"
However, you could switch to a parameter query instead and thereby avoid issues with quotes. Here is an untested example ...
Dim db As DAO.Database
Dim qdf AS DAO.QueryDef
Dim rs2 As DAO.Recordset
Dim mySQL2 As String
mySQL2 = "SELECT [qryFormation].* " & _
"FROM [qryFormation] " & _
"WHERE [WELL_ID] = [which_id]"
Set db = CurrentDb
Set qdf = db.CreateQueryDef(vbNullString, mySQL2)
qdf.Parameters("which_id") = CurrentID
Set rs2 = qdf.OpenRecordset
Use the Access help system to check functions, syntax, etc. in that code in case I made errors. Since you're new to Access, it will be to your advantage to get comfortable with its help system.
just wondering when using an expression on a form in a text box, to return a value from a table, can the expression have multiple tables in the expression to return the value?
the tables are linked and I can return the value in a query, so I figured that Access would be able to do it with this method as well????
=DSum("[tblMain]![Revenue]","tblMain","[tblMain]![Quarter]=3 AND [tblMain]![Region]='NorthEast'" AND [tblOffice]![Location]='NewYork'")
this is the expression that I entered into my text box, without the reference to the 2nd table it works fine, but once I had it, I get the flickering error message in the text box (just as on a report)......
I know this method is probably used more in reports than forms, but I am novice, and trying to come up with a dashboard solution that returns lots of facts quickly per department. I am using this in the "Control Source" field of the data tab of the properties window, not VB. Mainly because I do not know how to get it to work with VB.
Thanks for the help as always!
As far as I know, you cannot refer to more than one table or query in a domain aggregate function. As grazird says, how are these tables related? Let us say it is on tblMain ID, you can build a query called, say, qryMainOffice, the SQL (SQL View, Query Design window) would look something like:
SELECT [tblMain].[Revenue],[tblMain]![Quarter],[tblMain]![Region],
[tblOffice]![Location]
FROM tblMain
INNER JOIN tblOffice
ON tblMain.ID = tblOffice.MainID
DSum would then be (remove line break):
=NZ(DSum("[Revenue]","qryMainOffice",
"[Quarter]=3 AND [Region]='NorthEast' AND [Location]='NewYork'"),"Not found")
You could also use a recordset or query in VBA to return the value.
EDIT re COMMENT
To use the above in VBA, you either need to add parameters or use a string:
''Reference: Microsoft DAO 3.x Object Library
Dim rs As DAO.Recordset
Dim db As Database
Dim strSQL as String
Set db= CurrentDB
strSQL = "SELECT Sum(t.[Revenue]) As TotalNY" _
& "FROM tblMain t " _
& "INNER JOIN tblOffice o " _
& "ON t.ID = o.MainID " _
& "WHERE t.[Quarter]=3 AND t.[Region]='NorthEast' " _
& "AND o.[Location]='NewYork' " _
'' I have use aliases for simplicity, t-tblMain, o-tblOffice
'' If you wish to reference a control, use the value, like so:
'' & " AND [Location]='" & Me.txtCity & "'"
'' Dates should be formated to year, month, day
'' For joins, see http://www.devshed.com/c/a/MySQL/Understanding-SQL-Joins/
Set rs = db.OpenRecordset strSQL
If Not rs.EOF Then
Me.txtAnswer = rs!TotNY
Else
Me.txtAnswer = "N/A"
End If
You can also use different queries to return several results that can be shown with a list box or a subform:
strSQL = "SELECT TOP 5 o.[Location]," _
& "Sum(t.[Revenue]) AS TotRevenue" _
& "FROM tblMain t " _
& "INNER JOIN tblOffice o " _
& "ON t.ID = o.MainID " _
& "WHERE t.[Quarter]=3 AND t.[Region]='NorthEast' " _
& "GROUP BY o.[Location]"
The above would return revenue for quarter 3 for all locations in NorthEast region. If you want the top values of each group, you are looking at a more complicated query, which I will leave for now.
How are these tables related? Can you describe the relationship and any primary/foreign keys?
Also, referencing the table name is not necessary in the first parameter of this function (since it is already taken care of in the second one).
For example, your code could be:
=DSum("Revenue","tblMain","Quarter=3 AND Region='NorthEast'" AND [tblOffice]![Location]='NewYork'")
Just trying to save you some keystrokes and increase its readability. :)
I am new to Access. I have a table full of records. I want to write a function to check if any id is null or empty. If so, I want to update it with xxxxx.
The check for id must be run through all tables in a database.
Can anyone provide some sample code?
I'm not sure if you are going to be able to find all tables in the database with Access SQL. Instead, you might want to write up some VBA to loop through the tables and generate some SQL for each table. Something along the lines of:
update TABLE set FIELD = 'xxxxxx' where ID is null
Check out the Nz() function. It leaves fields unaltered unless they're null, when it replaces them by whatever you specify.
For reasonable numbers and sizes of tables, it can be quicker to just
open them
sort by each field in turn
inspect for null values and replace manually
It's good practice to find out where the nulls are coming from and stop them - give fields default values, use Nz() on inputs. And have your code handle any nulls that slip through the net.
I'm calling it the UpdateFieldWhereNull Function, and shown is a Subroutine which calls it (adapted from http://www.aislebyaisle.com/access/vba_backend_code.htm)
It updates all tables in the DbPath parameter (not tested, handle with care):
Function UpdateFieldWhereNull(DbPath As String, fieldName as String, newFieldValue as String) As Boolean
'This links to all the tables that reside in DbPath,
' whether or not they already reside in this database.
'This works when linking to an Access .mdb file, not to ODBC.
'This keeps the same table name on the front end as on the back end.
Dim rs As Recordset
On Error Resume Next
'get tables in back end database
Set rs = CurrentDb.OpenRecordset("SELECT Name " & _
"FROM MSysObjects IN '" & DbPath & "' " & _
"WHERE Type=1 AND Flags=0")
If Err <> 0 Then Exit Function
'update field in tables
While Not rs.EOF
If DbPath <> Nz(DLookup("Database", "MSysObjects", "Name='" & rs!Name & "' And Type=6")) Then
'UPDATE the field with new value if null
DoCmd.RunSQL "UPDATE " & acTable & " SET [" & fieldName & "] = '" & newFieldValue & "' WHERE [" & fieldName & "] IS NULL"
End If
rs.MoveNext
Wend
rs.Close
UpdateFieldWhereNull = True
End Function
Sub CallUpdateFieldWhereNull()
Dim Result As Boolean
'Sample call:
Result = UpdateFieldWhereNull("C:\Program Files\Microsoft Office\Office\Samples\Northwind.mdb", "ID", "xxxxxx")
Debug.Print Result
End Sub