Link to home
Start Free TrialLog in
Avatar of Painfree888
Painfree888

asked on

Counting Report - Questions about most efficient method


Hello Experts,

Have a question about the best way to generate a report. Background:

I have a table populated with customer orders. It's a fairly large table, field wise, and contains about 33,000 records, with about a 100 being added every day. The table contains fields such as Customer_Payment_Type, Customer_Shipping_Type, Customer_Shipping_Priority, etc. I have been requested to create a report that will display counts of the various values in the fields. For instance, my customer wants to know how many orders from Virginia we've had, how many orders from a certain city and how many orders on a certain date. So, the report will do nothing more than go through and count field entries.

One way to do this is to create a recordset based on the following query: reportRS = reportDB.openRecordset("SELECT * FROM Customer_Orders"). I say select * here because we'll basically be counting up everything in the table. Then, I thought it would be fairly easy to just loop through the recordset and do my counts, placing the final counts of each field into the report fields. Something like this:

Report_Activate
     Dim customerPaymentType_CreditCard As Long, customerPaymentType_Cash, customerShippingType_FedEx As Long,
     customerShippingType_UPS As Long, etc.
     Dim reportDB As DAO.Database, reportRS As DAO.Recordset

     Set reportDB = currentDB
     Set reportRS = reportDB.OpenRecordset("SELECT * FROM Customer_Orders")

     Do Until reportRS.EOF

         'Count the various types of payments used
          If reportRS!Customer_Payment_Type = "Credit Card" Then
                 customerPaymentType_CreditCard = customerPaymentType_CreditCard  + 1
          ElseIf reportRS!Customer_Payment_Type = "Cash" Then
                 customerPaymentType_Cash = customerPaymentType_Cash  + 1
         End If

         If reportRS!Customer_Shipping_Type = "Federal Express" Then
                 customerShippingType_FedEx = customerShippingType_FedEx + 1
          ElseIf reportRS!Customer_Shipping_Type = "UPS" Then
                 customerShippingType_UPS = customerShippingType_UPS + 1
         End If
     Loop

    'Counting is finished, now report on counts (write value of count variables to report fields)
     Me.customerPaymentType_CreditCard = customerPaymentType_CreditCard
     Me.customerPaymentType_Cash  = customerPaymentType_Cash
     etc.
End Sub

The example above is just a representative sample of what I'd like to do. I will need to count up a bunch of different field values. So, obviously, the above example won't work for me (well, actually, it will if I wanted to spend the time typing everything in, but I don't. Plus, I would imagine that using this method would be prone to really slowing down the more records that are added).

I was wondering if anyone had a better idea for how to do this. Perhaps writing various Count queries, or maybe something else. There must be a simpler (if not more simple, then more efficient) way to do this. Any advice would be greatly appreciated.

Thanks!
ASKER CERTIFIED SOLUTION
Avatar of Steve Bink
Steve Bink
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Obviously that is the most simplistic solution available.  You could also create a custom form to allow the user to pick the table, pick the field from a list of fields in that table, and otherwise simplify the criteria gathering into a menu-driven process.  Also note that there is no error checking in that sub...if the user enters a blank string, your best case is an incorrect return.  Worst-case is a runtime error.
Avatar of Painfree888
Painfree888

ASKER


Routinet,

Thanks for the info. I should have mentioned that my customer desires a canned report. The report is going to go through and count everything. The user will not be entering criteria into a form - the criteria will be hard coded into the report. Thus, my question about the most efficient means to do the report.  You may be asying to use a SQL count function to go through and get all of my counts (on about 75 fields). If that's the case, I kind of figured I'd have to do something like this.

I like the idea of the form overlaying a report, allowing users to supply the report criteria, though. Pretty nifty.
>>> I should have mentioned that my customer desires a canned report

No, you shouldn't have.  The customer's desire in this context means not a damn thing to the developer/administrator.  They want a report, you give them the ability to see a report.  If that SAME report can be used to 15-20 different requests of theirs, so be it...less work for you down the road, and it looks like you are a database GOD and on top of your job.

>>> allowing users to supply the report criteria,

Use a canned report, and you'll see why I suggested this model to start.  Think about it logically...they want to know, for example, how many orders want to come from Fredricksburg.  Ok, fine:

SELECT Count(*) FROM MyTable WHERE SourceCity='Fredricksburg'

No problem.  They can run that each and every day if they want.  But somewhere down the line, someone is going to pipe up and say "Hey, we used to get butt loads of orders from there, but now they all come from Washington...how do the numbers compare?"  So now you need another canned query, plus the original:

SELECT Count(*) FROM MyTable WHERE SourceCity='Washington'
SELECT Count(*) FROM MyTable WHERE SourceCity='Fredricksburg'

A few cities later, and your database is starting to get clogged with a whole bunch of miscellaneous crap that may or may not be needed.  Say Washington stops ordering, so you stop running the Washington query.  Six months down the road, someone else in Washington starts ordering.  Not remembering that there was a query already for Washington, you make another.  More bloat, more wasted space, more chance for corruption, more annoying for the admin.  It is best to start out flexible from the beginning...when the situation changes, you'll already be poised for it.  If you program for a narrow scope, you'll be stuck in a narrow scope.

If you STILL want to program for exact cases, then your scenario of a 75-field counting SQL statement comes to life.  Have fun with it, because it only gets worse from there.  That story ends with you coming back here asking how to develop the kind of system I'm suggesting to begin with, usually right after the 100th time they ask you to 'just change this little thing for me so I can see my report'.
The customizable report from a form is probably worth the effort.  Sounds like the customer won't know what he wants until he sees it.

Another way to hard code is to do domain aggregate functions.
so:
    Dim customerPaymentType_CreditCard As Long, customerPaymentType_Cash, customerShippingType_FedEx As Long,
     customerShippingType_UPS As Long
customerpaymenttype_creditcard = dcount("[customerpaymenttype]", "customer_order", "[customerpaymenttype]" = "CREDIT CARD")
customerpaymenttype_cash= dcount("[customerpaymenttype]", "customer_order", "[customerpaymenttype]" = "CASH")
customershippingtype_fedex=dcount("[customershippingtype]", "customer_order", "[customershippingtype]" = "FEDEX")
customershippingtype_ups=dcount("[customershippingtype]", "customer_order", "[customershippingtype]" = "UPS")

etc.

If you can build a table of fields and criteria, this could be used for your form, or as input to domain aggreate coding.

A suggestion for a single query you can use to get the data for your report.  This SQL is from a table with a field [Ref_Progress] that can have values of "Incomplete", "Ready To Go", "At Shop".  I think this is very similar to the table you are working with.

SELECT Sum(IIf([Ref_Progress]="Incomplete",1,0)) AS Inc, Sum(IIf([Ref_Progress]="At Shop",1,0)) AS [AS], Sum(IIf([Ref_Progress]="Ready To Go",1,0)) AS RTG
FROM tblRefurbPO;

The return is three values, one each for Inc, AS & RTG.

Obviously you'd need to add columns for all the fields you want to count but this should work.

OM Gang

Routinet,

Wow. I genuinely appreciate the advice. I should have been more detailed when I said this:

>>I like the idea of the form overlaying a report, allowing users to supply the report criteria, though. Pretty nifty.

I really should have said:

>>I REALLY REALLY like this idea! But, for my purposes, it MAY be more work than I want to do. The Access DB that I am working on is going to be replaced in the next four to five months by a J2EE app that is currently being developed. Right now, I am doing maintenance and fulfilling the odd pressing user request. This is one of those user requests. As this whole thing will be going away in the next few months, I was hoping to get something out quick and dirty (this is a high priority request) with a minimal investment in time (well, as minimal as I can make it - any approach is going to take time). If I were going to be developing a reporting framework for an application that was going to be in production for the next x # of years, I'd go with what you gave me, most definitely. But, it doesn't seem to me to make much sense to put this together in an app that's going to be gone by August. If I'm going wrong in my logical thinking here, please feel free to correct me. My assumption is that your way is better, but is going to require more time and work to implement on the front end.

Maybe I should have provided the context, but I didn't feel it was necessary at the time.

*****


LOL!  Then that changes things.  I still think the method I described is where you should aim...the coding is not that difficult for a generic report, and it can be amazingly flexible with very little setup.

Seeing as how you're working on a temporary project (THAT part you should have said before!  :þ), the canned query will work for you.  I do not envy your position over the next few months, though.  Same problems and issues, just with a definite deadline for worrying about them.  Any of the examples I posted before can serve as templates for what you want...modify the SQL according to your particular setup, and there you go.  If you'd like a specific query written, give us some info about the tables and we can whip it up for you.

routinet,

Your approach is by far the best, that much is agreed. And it might be worth doing just for the experience of doing it. I hadn't thought of this before. I have to say that I am having a bit of trouble visualizing exactly what it is you are describing. I can see the utility of it, for sure, but I do have questions. Never done anything like it. For instance, for a preset report (one which the users will want to see every day), it seems to me I'd still have to hand code the count queries. Otherwise, wouldn't I be forcing the users to go in and configure the report *each* time they want to run it (all 75 fields)? That would be a bit cumbersome for them. Ah, as I wrote this, I had a thought. I suppose, instead of coding the queries, I could have a checkbox on the form overlay that, when clicked, would preconfigure the actual form field values. This would have the added benefit of allowing the users to tweak the criteria if they so chose.

Note:

I developed this application with the understanding that it would be a temporary solution until something bigger and better came along (intially. That was almost a year ago. There are many things I wish I had done better, and would have had I known how long this stupid Access app was going to be around. With that history in mind, it's quite possible that the 4 to 5 month replacement date could actually much farther out than I think. My only problem now is that the users want this report ASAP, and I figured, balancing app longevity against flexibility and future maintenance needs, the simple query solution would be better. From my experience with this project, I definintely see the wisdom in what you are saying, believe me. I've coded myself into more holes than I can count, all because of time pressures and a mistaken belief that the thing was going away soon.

My apologies for not providing the proper context in the beginning. You've been a great help to me more than once, and I certainly appreciate it.
With the form idea, imagine the user wanted an order count based on varying criteria.  Some of the fields could be city of origin, state of origin, amount of purchase, or even product type.  Now imagine you have a form with a combobox and a textbox.  When you open the form, the combobox populates with a list of fields found in the orders table.  The user selects a field, then selects what that field should equal to be included in the report.  So say the user selects "city" and types "Washington" in the text box:

sSQL = "SELECT count(*) FROM MyOrderTable WHERE [" & Me.MyComboBox & "] = '" & Me.MyTextBox & "'"
Set rs = CurrentDb.OpenRecordset(sSQL)

The sSQL variable will hold the SQL you can pass to any report as the query upon which it should be based, and the rs variable holds a recordset if you would like to process it with code.  This is rather simplistic, but for simple reports it works excellently.  Through very little effort, you could expand this to include 2 or more fields in the criteria as well...the limit of the scope is only the limit to your patience in coding it.  :)
Make textboxes in the detail section for every value you want to count. In the controlsource
=GetTotals("WhatYouWantToDo";"Fieldname";"Criteria")

add the funtin to the report:
Public Function GetTotals(strFunction As String, strField As String, _
    strFilter As String)




End Function
Ouch!! A typo sent this tooo soon! I'm sorry!

Make textboxes in the detail section for every value you want to count. In the controlsource
=GetTotals("WhatYouWantToDo";"Fieldname";"Criteria")

add the funtin to the report:
Public Function GetTotals(strFunction As String, strField As String, _
    strFilter As String)

Dim strSql as string
Dim rs as dao.recordset

strSql = "SELECT " & strFunction & "([" & strField & "]) AS Answer " _
    & "FROM Customer_Orders " _
    & "WHERE [" & strField  & "]" & strFilter

Set rs = CurrentDb.OpenRecordset(sql)
GetTotals = nz(rs!Answer, 0)
Set rs = Nothing

End Function

Examples of Controlsources:
=GetTotals("Count";"Customer_Payment_Type";"='Credit Card'")
You could also use if you want to make summaries
=GetTotals("Sum";"Customer_Payment";">0")

If you always will count:
In control
=GetTotals("Customer_Payment_Type='Credit Card'")

and in code
strSql = "SELECT Count(*) AS Answer " _
    & "FROM Customer_Orders " _
    & "WHERE " &  strFilter

I'm not shure this is complete, but may be you could get some ideas to make the right variations for your pourpose.

/Svein Erik

A lot of great input. Still deciding which way to go with all this. Be back in a bit to divvy up points once I decide.

OK, went with routinet's idea and got it up and running. Many thanks to all who participated!