Monday, February 28, 2005

Basing one combo box on another

This technique is very useful in applications where you want to allow your users to choose multiple data from combo boxes - when you want one combo box to display records based on what the user selected in the first combo box.

Microsoft has put together a very small sample database of how this can be accomplished.

Friday, February 25, 2005

WinZip - Outlook Companion

Winzip has long been the standard tool for compressing files and folders. With Outlook Companion, Winzip is offering tighter integration with Outlook.

From Winzip's website: Zipping attachments saves transmission time, conserves space in Outlook mailboxes, and saves disk space for both the sender and recipient of the e-mail. The Companion can be configured to automatically zip your attachments, ask whether or not you want them zipped, or let you zip and attach files manually with just a few mouse clicks. Toolbar and menu items allow you to control the Companion's functionality on a message-by-message basis. In addition, sensitive attachments can be easily protected with the same built-in advanced AES encryption found in WinZip 9.0

Benefits
  • Easily zip attachments when sending Outlook e-mail messages
  • Conserve space in your Outlook mailbox
  • Save disk space
  • Save transmission time
  • Protect sensitive attachments with encryption
  • Easy-to-use; just install it and it is ready to work for you

WinZip - Companion Product Information

Problem with record selection and left joins

Many report writers begin to explore table joins in Crystal Reports when they aren't getting the results they expect. While changing a join from an inner to an outer is sometimes the easy solution, it may not always yield the results you expect. Imagine you want all your customers in a report, whether they had transactions or not. Where they do have transactions, you are only interested in a selection of transactions

To illustrate with a simple example, let's look at a customer table and a transaction table of orders.

This one-to-many table relationship is common to many reports. This is a problem we have seen on many occasions. There are several solutions you might like to consider if your report has this problem. The default Inner Join will give us only those customers who have orders. To get ALL customers and their orders if they have any, you would use a Left Outer join. Under normal circumstances, the Order table fields will be NULL for those customers without any orders. And that is the foundation of the problem we are about to describe.

Usually, you don't want all the records in the transaction table. At a client site recently, we only wanted the transactions from a selected date range. But we still wanted to see all the customers on the reports. Because we are doing a selection on the date field in the orders table, we restricted our customer selection to only those customers who had orders in the selected dates. You might suggest that those customers without any orders will have NULL in the fields in the order table, so select the NULL values as well. Sure, that will pick up customers who have NEVER had any orders. But customers who have orders, but not in the date selection are still missing from the report. This is because the LEFT join is done in the database prior to record selection. We need a solution that shows all customers, and their orders in the date selection (if they have any). There are several solutions to this. Thanks to Tim Wilson for his help investigating these.

Brute Force Solution

Use a formula on all records After examining and trying some of the other methods, this was the solution we implemented. We expected a performance hit, but the report was fast and gave us the results we wanted.

As faster and faster processors are delivered it is time we used some of that extra power for more productive purposes than 3D helicopter games. We joined the two tables with a left join, but with no selection from the orders table.

Yes, all the transactions were loaded into the report and had to be processed. We calculated our “This Month” total using a summary of an If .. Then .. Else formula.

A Running Total Field would also work fine here. You might try and avoid a report that loads millions of records into one report. But Crystal can handle the volume without any problems. This solution performed well and worked with our CR 8.5 user.

Stored Procedure Solution

We looked at the previous problem from the database side. If Crystal wasn't part of the solution, how would we do this using SQL? The raw data tables won't do what we require, so we need an SQL stored procedure.

* We looked into using SQL to create the left join we needed, and it just didn't happen. If any of you SQL experts out there have a pure SQL solution for this, then let us know.
* If only summary data are required, a sub SELECT would work. But if you need the transactions as well, it is not enough.
* A UNION join also looked like it might provide a solution. With a stored procedure we could select the required transactions from the orders table. Then we could use Crystal Reports 9 or 10 to left join the stored procedure to the master table. But this solution is not available for CR8.5 or earlier and has a big performance hit.

Business View Solution

Business Views are a new feature in Crystal Reports 10. We created a SQL command with the date selection parameters inside a Business Foundation. Left join that to the customer table, and create the required Business Elements and Business Views.

This worked fine in our Crystal Reports 10 test report.

SELECT INTO Solution

One stored procedure solution we thought would work was to use a stored procedure with several steps. The first step did a SELECT INTO to create a temporary table with the only the records we needed, and then it left joined the temporary table to the master table. This would work with any version of Crystal Reports.

The negative side of this solution is that the report can only be run by one user at a time. A second user would reset the temporary table while the first was still using it.

Subreport Solution

Back into Crystal Reports with a main report from the master table, and a subreport for each transaction record to get the data we required. This did work correctly.

But the performance and selection of the transactions for each master record made this the slowest performing solution of any we implemented. Also, you could not do any grouping or primary record selection on fields in the transaction table.

Conclusion

Use the solution that best suits your needs. Each of the above has specific technology and performance limits. But we now have several ways to get that report with the data we want on it.

This article is copyrighted by Crystalkeen, Mindconnection, and Chelsea Technologies Ltd. It may be freely copied and distributed as long as the original copyright is displayed and no modifications are made to this material. Extracts are permitted. The names Crystal Reports and Seagate Info are trademarks owned by Business Objects.

Thursday, February 24, 2005

Retrieving random records with a query

I've had several students in the past ask about getting random results from a query, so they could review/analyze random data from there tables. For instance, one student was working with a customer service database - tracking calls and solutions provided to customers. Once a week, one person was in charge of calling random customers to get feedback on how the customer service was handled and if the customer was satisfied with the service. The employee would open the database and manually look for random customers to call. This solution provides an easy way to build a query - which could then be formatted with a report - to automatically build a list of any number of customers randomly.

Type the following expression into an empty column in the field row of the query design view:

RandomValue: Rnd([field])

where [field] is any field in the table that contains numeric values - the field can be an AutoNumber, Number, or Date/Time field. Apply a sort order to the RandomValue field, ascending or descending. Use the Top Values dropdown list on the Query Design toolbar to specify how many records to return. If you want a number or percentage that isn't listed, simply enter the desired amount in the Top Values text box. When you run the query, you'll find that Access returns a random selection from the underlying record source.

Wednesday, February 23, 2005

Outlook reminders sent to your mobile phone

I'm always forgetting stuff. Meetings, appointments, tasks, etc. I forget them all. I don't want to look like batman with a utility belt so instead of carrying a cell phone and pda, I wish Outlook could send a text message to my phone as a reminder if I'm away from my desk. This great little utility - although it's not free - makes a great add-on for Outlook.

OutlookSMS

Thursday, February 17, 2005

Add bullets to Access reports

Bullets are useful to draw the reader's attention to a particular item, displaying lists, or even separating address parts in the footer of a report (similar to most company letter head). You can add a bullet to a report in Access.

Open your report in design view.

Turn on the Number Lock.

Insert a label and type the text you want in the Caption property. When you want to insert a bullet:

Hold the alt key down and--

For a round bullet, type:

0149

For a hyphen bullet, type:

0150

Release the alt key.

Wednesday, February 16, 2005

Crystal Reports XI Released

Business Objects has just released the next version of Crystal Reports 11 and Crystal Enterprise 11. Unlike the upgrade to version 10, the upgrade to version 11 has some significant improvements for report designers. Here are the ones I feel are most significant (follow this link to see all new features):

Dynamic Parameters:
Crystal parameters can finally generate the list of default values at runtime. The 'pick list' is no longer static.

Crystal Reports allows you to generate the 'pick list' directly from your data. You can specify a field in the report and Crystal will query the database at runtime for current values. You can even create a command (SQL statement) to generate the list.

Cascading Parameters:
You can now have one parameter prompt determine the list in another prompt. For instance, when trying to select an employee you could first select a department. Then Crystal would show you a list that only included employees from that department.

I think Business Objects was finally feeling the pressure from Microsoft's SQL Server Reporting Services. I expect this new rivalry to really benefit developers as the business intelligence tools gain more and more features.

Microsoft Access Limitations

Many times in class students will ask when they should upgrade to a server product like SQL Server for their databases. Access does have limitations; this list can be useful when deciding whether to use Access or some other database.

Access table limitations:

Number of characters in a table name 64
Number of characters in a field name 64
Number of fields in a table 255
Number of open tables 2048. The actual number may be less because of tables open internally by Microsoft Access.
Table size 1 gigabyte
Number of characters in a Text field 255
Number of characters in a Memo field 65,535 when entering data through the user interface;
1 gigabyte when entering data programmatically.
Size of an OLE Object field 1 gigabyte
Number of indexes in a table 32
Number of fields in an index 10
Number of characters in a validation message 255
Number of characters in a validation rule 2,048
Number of characters in a table or field description 255
Number of characters in a record (excluding Memo and OLE Object fields) 2,000
Number of characters in a field property setting 255

General Access limitations:

Microsoft Access database (.mdb) file size 2 gigabytes. However, because your database can include linked tables in other files, its total size is limited only by available storage capacity.
Number of objects in a database 32,768
Modules (including forms and reports with the HasModule property set to True) 1,000
Number of characters in an object name 64
Number of characters in a password 14
Number of characters in a user name or group name 20
Number of concurrent users 255