Show MS Access Subreports With No Data

On October 29, 2009, in Databases, by murat

On MS Access applications, usually I use subreport objects with SQL queries of their own, linked to the main report which includes the subreport. Classic example of such master-detail reports is invoices:

+ master (main report) section includes invoice date, invoice number and details of the customer account such as name and address lines.
+ detail (subreport) section includes the products and services charged, product code, desription, quantity, price, and line total etc.

Another example could be statement of share transactions of an employee within a date interval.

+ employee record (name, surname, NI number etc) on main section
+ share transaction list in a subreport underneath the employee record

The problem: When the subreport has no data, MS Access does not show it at all. I may want to see the column headings, at least, or a message with it.

The Solution: We can modify the SQL query (record source) of the subreport and make sure that it has data every time it is run.

Let’s say the SQL query of the main report is something like this:

SELECT e.emp_surname , e.emp_name, e.payroll_number
FROM tbl_employee e
WHERE e.payroll_number = GetCurrentEmployeeNumber()

And the original SQL query for our subreport which brings the share transactions of employees is:

SELECT t.trans_date, t.quantity, t.price, t.description, t.payroll_number
FROM tbl_transaction t
INNER JOIN tbl_employee e
  ON t.payroll_number = e. payroll_number
WHERE e.payroll_number = GetCurrentEmployeeNumber()
  AND t.trans_date BETWEEN GetStartDate() AND GetEndDate()

We can convert it into a UNION query which will always produce data:

SELECT t.trans_date, t.quantity, t.price, t.description, t.payroll_number
FROM tbl_transaction t
INNER JOIN tbl_employee e
  ON t.payroll_number = e. payroll_number
WHERE e.payroll_number = GetCurrentEmployeeNumber()
  AND t.trans_date BETWEEN GetStartDate() AND GetEndDate()
 
UNION
 
SELECT TOP 1
  NULL AS trans_date, 0 AS quantity, 0 AS price, "N/A" AS description, e.payroll_number
FROM tbl_employee e2
WHERE e2.payroll_number = GetCurrentEmployeeNumber()
  AND e2.payroll_number NOT IN (
    SELECT t2.payroll_number
    FROM tbl_transaction AS t2
    WHERE t.trans_date BETWEEN GetStartDate() AND GetEndDate()
    GROUP BY t2.payroll_number
    HAVING COUNT(*)>0
);

This way, when there is at least one share transaction the subreport will be visible as usual, listing the transactions of the employee. If there are no transactions, the second part of the union query will have the information we want to show.

Note: In my SQL queries, I referred to my public functions placed inside a VBA module; GetCurrentEmployeeNumber(), GetStartDate(), GetEndDate(). That’s my way of collecting and holding the user inputs as criteria affecting my query results, using global variables; basically, setting them and getting them when required.

Tagged with:
 

Stock Kid 2009

On May 29, 2009, in Databases, Programming, by murat

Another free database application from Argeus: Stock Kid 2009. Download and see it for yourself if it can help you track your inventory and costs.

Tagged with:
 

VAT Kid 2009

On May 29, 2009, in Databases, Finance, Programming, Windows, by murat

Argeus Solutions is now giving away a free database application: VAT Kid 2009. You can download and use it for free! It is useful for small companies to enter all sales and purchase transactions without any details and get VAT return form quickly and easily.

Snapshot of VAT Kid 2009

Microsoft Office Access 2003 is required. Please contact me if you are using an earlier version of MS Access. If you do not have Access, you can use Access Runtime 2007.

Tagged with:
 

The problem:
When we use formulas on textboxes in the report footers of MS Access reports, such as “=sum(a_field_in_header)”, we get incorrect results because of duplication of same amounts for multiple items in detail section.

Here is an example to make it clear:

Sample table header and detail with data

We have two tables one for header and another detail records, for example, invoice header and a item details.

SELECT h.*, d.item, d.price, d.quantity, d.detail_subtotal, d.detail_vat, d.detail_total
FROM t_trans AS h
INNER JOIN t_trans_detail AS d 
ON h.trans_id = d.trans_id;

So, that we get:
query

If we design a report to show the transactions:

We will get a listing of grouped transactions like this:

The report footer shows the problem and solution includes some VBA code below to do the correct calculations.

Option Compare Database
Option Explicit
 
Dim myTrans As New Collection
Dim mySums(1 To 2) As Double
'1 sum of total cost
'2 sum of grandtotal
'distinct transactions

Private Sub myCalculation(lTransID As Long, myCost As Currency, myTotal As Currency)
On Error Resume Next
    myTrans.Add lTransID, "T" & lTransID
    'try to insert transaction ID with collection Key
    If Err Then 'exists
        Err.Clear
    Else 'first time, so consider distinct headers
        mySums(1) = mySums(1) + myCost
        mySums(2) = mySums(2) + myTotal
    End If
End Sub
 
Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
    Debug.Print "Trans ID: " & Nz(Me.trans_id, 0)
    'you will see this subroutine is called more than once
    Call myCalculation(Me.trans_id, Nz(Me.txt_totalcost, 0), Nz(Me.txt_grandtotal, 0))
End Sub
 
Private Sub ReportFooter_Format(Cancel As Integer, FormatCount As Integer)
    Me.txt_sum_totalcost = mySums(1)
    Me.txt_sum_grandtotal = mySums(2)
End Sub
Tagged with:
 

Update Query on MS Access

On June 20, 2008, in Databases, by murat

Microsoft programmers are always very particular on their way of approaching problems. Since they are the “biggest” software developers in the world, they have to set the standards!?

Problem: Updating field(s) of a table using a complex (sub) query.

Solution:

1. In standard SQL, you can this:

1
2
3
4
UPDATE TABLE1 
SET A_FIELD = Q.CALCULATED_FIELD
FROM A_COMPLEX_QUERY Q
WHERE TABLE1.ID_FIELD = Q.ID_FIELD;

It makes sense, right?

No, not according to MS Access!

2. You must try something like this:

1
2
3
4
UPDATE TABLE1
INNER JOIN A_COMPLEX_QUERY Q
ON TABLE1.ID_FIELD = Q.ID_FIELD
SET A_FIELD = Q.CALCULATED_FIELD;

However, it says:

“You must use an updatable query!”

3. One of the “good” features of MS Access is its functions.

1
2
3
UPDATE TABLE1 
SET A_FIELD = 
DLOOKUP("CALCULATED_FIELD", "A_COMPLEX_QUERY", "ID_FIELD = " & ID_FIELD);

Simple; but it may take ages execute if you have thousands of records.
You can also write your own (public) functions in VBA modules and refer to them in anywhere inside your MS Access database.

Tagged with: