Use SQL commands to solve report problems and speed Crystal Reports

Adding SQL commands to Crystal Reports can cut network traffic, improve efficiency and help you create complex reports that might otherwise not be possible.

By Mike Lewis

In an earlier article, I showed you how SQL expressions can be used to speed up Crystal Reports. This time, we'll switch the focus from SQL expressions to SQL commands. What's the difference? In a nutshell:

Like SQL expressions, SQL commands can greatly speed up your reports. But they have another important advantage: They open the door to all kinds of complex reporting tasks that might otherwise be difficult or impossible to achieve.

In general, a SQL command can consist of any query that the database understands. So it can contain unions, sub-queries, projections, aggregations and groupings, TOP N clauses, and even calls to functions or stored procedures on the server.

Drawbacks

The obvious disadvantage of SQL commands is that you need to be familiar with Structured Query Language - and with the particular flavor of SQL that your back-end database understands. If you've never coded a SELECT statement before, using these commands in CR will be a challenge. On the other hand, it could also be an excellent incentive to start learning SQL, given the ability it will give you to develop more complex reports.

Figure 1

Figure 1: A report showing grouping and aggregation.

Example 1: Grouping and aggregation

To give you an idea of how to use SQL commands - and how effective they can be - let's look at a simple example. We'll create a report that shows customer IDs, along with the total order value for each customer (Figure 1). We'll assume that all the required data can be obtained from the Orders table.

To create this report without using SQL commands, you would place the customer IDs and the individual order values in the Details band. You would then group the report by customer ID, add a summary field (of type "sum") to the Group 1 Header band, and finally hide the Details band so that only the IDs and sums were visible.

To create the same report with the help of a SQL command, you would define the grouping and summary, not in the report, but as a query. In other words, you would write a SELECT statement that looks something like this:

SELECT CustomerID, SUM(Order_Amount) AS Total
  FROM Orders
  GROUP BY CustomerID

Because the query is executed by the database server, only the total rows - that's one row per customer - will be sent across the network. By contrast, if you weren't using a SQL command, the grouping and totaling would be done in the report, so the server would have to send every order across the network. Since network traffic is usually a major bottleneck, the performance benefit of using a SQL command can be dramatic.

The mechanics

Figure 2

Figure 2: Double-click Add Command
to open the editing window.

How do you go about adding a SQL command to your report? Here are the steps:

  1. Start, as usual, in either the Database Expert or the Data page of the Standard Report Wizard.
  2. Choose your data source in the usual way, for example by drilling down into Favorites, Current Connections or Create New Connection. Log onto the data source if necessary.
  3. Under the database's node, double-click on Add Command (Figure 2).
  4. You will now see an editing window, like the one in Figure 3. Type (or paste) your SQL command in the large edit box in the left of this window. Then click OK.

At this point, Crystal Reports will send the command to the database server, which will try to execute it. If the server detects an error, it will send a message back to CR, which will display the message and refrain from saving the command. You must deal with the error before you can go any further. (Keep in mind that CR sends the command exactly as you enter it, so be sure that the syntax is correct for the specific database you are using.)

Figure 3

Figure 3: The SQL command editing window.

If the command is error-free, the server will send a result set back to the report. The result set will appear under the Database Fields node in the Field Explorer. It appears as a virtual table, named Command, which contains exactly the rows and columns that your query requested (Figure 4).

You can now treat this virtual table just like any other table. In particular, you can drag its fields onto the report, and use them in sorting, grouping and selection criteria, in Crystal formulas, or anywhere else where CR expects a field name.

Editing a command

Figure 4

Figure 4: The query results
appear as a virtual table.

After you've set up your SQL command in this way, you can go back and edit it at any time. To do so, open the Database Expert, right-click on the Command node in the Selected Tables box, and choose Edit Command.

Example 2: Using a UNION

In the example we just looked at, the main reason to use a SQL command was to speed up the report. But SQL commands have another major benefit: They allow us to perform complex reporting tasks that might not otherwise be possible.

To illustrate, let's assume that you have two customer tables: one for your current customers, and another for your archived customers. You want to create a report that shows the names and phone numbers of all customers, regardless of whether they are current or archived.

These tables aren't related in any way, so you can't simply bring them into the report and link them together. One solution would be to create two sub-reports, one for each of the tables. But that would only work if you were happy to keep the two sets of customers separate. It would be no good if you wanted to produce a single customer list, for example showing all customers in customer name order.

With a SQL command, the problem is easy to solve. You use a UNION clause to combine the two tables. Your query would look something like this:

SELECT Lastname, Firstname, Phone
  FROM CurrentCustomers
UNION SELECT  Lastname, Firstname, Phone
  FROM ArchivedCustomers
  ORDER BY Lastname, Firstname

The result is a virtual table that contains both types of customer, in the required order. Crystal Reports won't know or care whether the customers are current or archived; it simply sees a single, unified table.

Adding parameters to a SQL command

In many cases, you will want to parameterize your SQL command, that is, you will want to prompt the user for values that are to be used in the query. As an example, in the totals per customer report we looked at earlier, you might want to only show customers whose total order value exceeds a certain amount, that amount to be supplied by the user when viewing the report.

Figure 5

Figure 5: Setting up a parameter.

To achieve that, go back to the command editing window shown in Figure 3. Click the Create button to the right of the Parameter List box. You will see a dialog like the one in Figure 5. This asks for four items of information: a name for the parameter, the text that the user will see when prompted for a value, the data type of the value, and a default value (only the first and third of these are mandatory).

When done, click OK. The parameter will now appear in the Parameter List box within the editing window.

The next step is to edit the command (in the left-hand box) so that it includes the parameter. To do so, place the insertion point where you want the parameter to appear. Then double-click on the name of the parameter in the box to the right. The parameter will appear in the command in the usual CR parameter syntax, that is, preceded by a question mark and enclosed in braces.

So, staying with our total orders per customer example, if we assume the parameter is named Cutoff, the query would look like this:

SELECT CustomerID, SUM(Order_Amount) AS Total
  FROM Orders
  GROUP BY CustomerID
  HAVING SUM(Order_Amount) > {?Cutoff}

When the user runs or refreshes the report, they will see the standard CR parameter prompt, inviting them to enter a value for the cutoff figure. The value they enter will then be passed to the command, which will in turn pass it to the server.

You can have as many parameters as you like in a command. Once you've created a parameter, you can edit it by clicking on its name in the SQL Command window and then clicking the Modify button. The parameter will also appear under the Parameter Fields node in the Field Explorer, from where it can be modified just like any other report parameter.

Example 3: A more complex query

To finish, let's look as something a little more complicated. In an earlier article, I showed a SQL Server query that will find all addresses within a given radius of a given UK postcode (although the technique is specific to the UK, the principle can be adapted for other countries). Using this technique, you could produce a report that shows, for example, all customers within 25 km. of a given branch office, or all suppliers within 100 miles of your base.

The query is complicated by the fact that it calls a user-defined function (dbo.GetDistance) to determine the distance between a pair of addresses. It also calls a couple of built-in T-SQL functions to extract the relevant part of the target address's postcode. However, those complications are hidden within the SQL command and are therefore of no concern of Crystal Reports. The program simply sees a virtual table containing the addresses that are to be shown in the report.

To produce this report, you need to create two parameters: one for the outcode of the starting point (the branch office, or whatever) and one for the required radius. Remember, the radius must be expressed in the same units as the eastings and northings in the postcode lookup table. (Outcodes, eastings and northings are all explained in the earlier article.)

You can now write the SQL command, which will look something like this (I simply pasted this code from the earlier article, substituting the parameters for the hard-coded values):

DECLARE @Start char(5), @StartE int, @StartN int, @Radius int

SET @Start = '{?StartPoint}'  -- outcode of start point
SET @Radius = {?Radius}  -- radius (same precision as the parameters)

-- Get easting and northing of start point
SELECT @StartE = x, @StartN = y 
  FROM Postcodes 
  WHERE @Start = Outcode

-- Get addresses within @Radius units of start point
SELECT a.Address 
  FROM Addresses a 
  JOIN Postcodes p 
  ON p.Outcode = LEFT(a.Postcode, CHARINDEX(' ', a.Postcode))
  WHERE dbo.GetDistance(@StartE, p.x, @StartN, p.y) <= @Radius

As you can see, this query is more like a SQL Server script, with variable declarations, SET statements, two separate SELECTs, and the various functions calls. None of this is a problem. Provided the server can execute the code and return a result set, you can use the code in a SQL command.

Final word

One of the best things about SQL commands is that they can be used with virtually all databases. Provided your database understands SQL - and the vast majority of them do - you'll get the benefits. If you're using a file-based database, like Access or Visual FoxPro, you won't necessarily see the same performance boost, but you will still be able to use SQL commands to perform complex queries and therefore simplify your reports.

If you're comfortable writing SQL code - or if you're willing to learn - you'll find that SQL commands are a very powerful tool. Used properly, they let you delegate complicated data-handling tasks to the database where they belong, and to focus your efforts on using CR for what it does best: producing great-looking reports.

February 2012

Please note: The information given on this site has been carefully checked and is believed to be correct, but no legal liability can be accepted for its use. Do not use code, components or techniques unless you are satisfied that they will work correctly with your sites or applications.

If you found this article useful, please tell your friends: