Grouping Output

At times, the ability to group ColdFusion output data can make your presentation much cleaner and easier for your visitors to understand. 

Consider for example, a Web site that displays information about authors and books would probably want to display their information in a format like:

Author Name:

  • book name
  • book name

Author Name:

  • book name
  • book name


At the very least, they would likely have a database with the following two tables: tblAuthor and tblBook, which would look like:



Notice the relationship between the two tables. authorID is the primary key in the tblAuthor table. It is a unique identifier, and each value can only appear once. authorID also appears in the tblBook table. In this case, it is a foreign key and each value can appear many times. This is known as a one-to-many relationship between tables. Each one author can have many books.

Now, let?s say we want to query the tables to grab the author and book title information.

<cfquery name=?getAuthorsAndBooks? datasource=?myDSN?>
        SELECT      tblAuthor.authorID, 
                        tblAuthor.authorName,
                        tblBook.bookID,
                        tblBook.bookTitle
        FROM         tblAuthor INNER JOIN tblBook ON tblAuthor.authorID = tblBook.authorID
        ORDER BY   tblAuthor.authorName, tblBook.bookTitle
</cfquery>

The results might not be what you expect. 


As you can see in the <cfdump> above, each author?s name is output multiple times (once for each book they?ve written). Recalling our desired output (from the beginning of the tutorial), we might try to do the following:

<cfoutput query=?getAuthorsAndBooks?>
    <b>#authorName#</b>
    <ul>
        <li>
#bookTitle#</li>
    </ul>

</cfoutput>

But because our query returns each author multiple times (once for each book he or she has written), we get the following output:

Ben Forta 

  • ColdFusion MX Web Application Construction Kit 

Ben Forta 

  • Teach Yourself SQL in 10 Minutes

J. K. Rowling 

  • Harry Potter and the Chamber of Secrets 

J. K. Rowling 

  • Harry Potter and the Prisoner of Azkaban 

J. K. Rowling 

  • Harry Potter and the Sorcerer's Stone 

Steven King 

  • Carrie

Steven King 

  • Cujo 

Steven King 

  • The Shining

William Shakespeare 

  • Julius Caesar

William Shakespeare 

  • King Lear 

William Shakespeare 

  • Romeo and Juliet

William Shakespeare

  • The Taming of the Shrew 

This is obviously, not what we want. We?re looking to get the following output:

Ben Forta

  • ColdFusion MX Web Application Construction Kit
  • Teach Yourself SQL in 10 Minutes

J.K. Rowling

  • Harry Potter and the Chamber of Secrets
  • Harry Potter and the Prisoner of Azkaban
  • Harry Potter and the Sorcerer?s Stone

Steven King

  • Carrie
  • Cujo
  • The Shining

William Shakespeare

  • Julius Caesar
  • King Lear
  • Romeo and Juliet
  • The Taming of the Shrew

We can probably do it in two queries...one to query the author table and another to query the book table...but that?s twice as many trips to the database as we need. We can do it with our original query, using the data displayed in the <cfdump> above.

We?re going to use the GROUP attribute of the <cfoutput> tag. In our query above, we did:
<cfoutput query=?getAuthorsAndBooks?>


Now let?s change it just a bit. It?s going to change to:
<cfoutput query=?getAuthorsAndBooks? group=?authorname?>

As this would imply, it directs ColdFusion to display the output in groups, rather than once for each record. In this case, the column to group on is authorname. 

Think of a group as if you had each of these books in front of you. If you put each book into a separate pile (based on author), you would have effectively grouped each book. You would have a pile for Ben Forta, a pile for Shakespeare, etc. Each pile represents a group. While each pile (group) has many books in it, there is only one pile per author.

So if <cfoutput query=?getAuthorsAndBooks? group=?authorname?> defines the ?piles?...how do the piles get populated? Interestingly enough, with a nested <cfoutput> tag (the only time <cfoutput> tags can be nested is when a GROUP attribute is used on the outer tag). 

Here?s the whole output:

1. <cfoutput query="getAuthorsAndBooks" group="authorname">
2. <b>#authorName#</b>
3. <ul>
4. <cfoutput>
5. <li>#bookTitle#</li>
6. </cfoutput>
7. </ul>
8. </cfoutput>

...and a line-by-line explanation:

Line 1: Tells ColdFusion that the output will be grouped (again, think piles), and what column to group on.
Line 2: Outputs the value of each pile. Imagine putting an index card with the name of the author in front of each pile to let people know the contents of the pile (eg this pile is Steven King, this pile is Shakespeare, etc).
Line 3: Since the books are going to be displayed in a bulleted list, we start a <ul> (HTML unorderd list)
Lines 4-6: The nested <cfoutput> tag. This is where the ?piles? actually get filled up. ColdFusion is looping over each record (remember, a <cfoutput> over a query is simply a loop over the query results). At the start of this tutorial, I showed a <cfdump> of the query. Each record contained the author name, and the book title. As ColdFusion is looping over that output, it simply takes each book title, looks at the author name, and places it in the appropriate ?pile?.

This produces the desired output. Each author name is displayed only once, with the list of books they?ve written displayed under their name.

You should now have a full understanding of how to group your output. You can stop reading now...or dare to continue, where I?m going to try and thoroughly confuse you :)

You see...can also group multiple times. If the book database we used in this tutorial were a database for an actual library or bookstore, there would be much more detail involved. Genre would likely be part of the database. If that were the case, you could group on genre, then author, and then list the books. 

To continue with the piles analogy...let?s say there were circles on the ground. Each circle represented a genre. Within the ?suspense? circle, would be a pile for Steven King, a pile for Clive Barker, etc. Within the ?classics? circle, there would be piles for Shakespeare, Chaucer, etc. It?s just a larger group containing a smaller group that ultimately contains individual items.

I?ve added one additional author to the authors table, and two additional books to the books table...along with an additional column in the books table (genre). The one-to-many relationship still exists. So our tables now look like:



Adding the new column to our query, we now have:

<cfquery name="getAuthorsAndBooks" datasource="group">
    SELECT                tblAuthor.authorID, 
   
                                  tblAuthor.authorName,
                              tblBook.bookID,
                              tblBook.bookTitle,
                              tblBook.genre
    FROM                   tblAuthor INNER JOIN tblBook ON tblAuthor.authorID = tblBook.authorID
    ORDER BY             tblBook.genre
                              tblAuthor.authorName,
                              tblBook.bookTitle
</cfquery>

Notice we also added the new column to the ORDER BY clause of our SQL statement. When grouping output, the column on which you?re grouping must be included in the ORDER BY. Not following this rule will not generate an error...but will almost certainly produce an unexpected output.

Our new query produces the following recordset:



As you can see, the only difference is the additional column for genre, as well as the 2 extra records returned (authorname J.R.R. Tolkien).

This is our desired output:

Classics

  • William Shakespeare
    • Julius Caesar 
    • King Lear 
    • Romeo and Juliet 
    • The Taming of the Shrew 

Fantasy

  • J. K. Rowling 
    • Harry Potter and the Chamber of Secrets 
    • Harry Potter and the Prisoner of Azkaban 
    • Harry Potter and the Sorcerer's Stone 
  • J.R.R. Tolkien 
    • Lord of the Rings 
    • The Hobbit 

Reference 

  • Ben Forta 
    • ColdFusion MX Web Application Construction Kit 
    • Teach Yourself SQL in 10 Minutes 

Suspense 

  • Steven King 
    • Carrie 
    • Cujo
    • The Shining


You can see that the outermost group is genre (the circles on the floor). Our ?piles? are intact, as each ?circle? contains a pile defined by author (see the Fantasy group, which contains J.K. Rowling and J.R.R. Tolkien). And then of course, each individual book in each of the piles.
The code to achieve this is:

<cfoutput query=?getAuthorsAndBooks? group=?genre?>
    <u>#genre#</u>
    <ul>

    <cfoutput group=?authorName?>
        <li><b>#authorName#</b></li>
        <ul>

        <cfoutput>
            <li>#bookTitle#</li>
        </cfoutput>
    </ul>
    </cfoutput>
    </ul>
</cfoutput>

I?m going to forego the line-by-line explanation, as it?s very similar to the code sample showing the output grouped by author. It should be self-explanatory.

And that?s where I?m going to bid you adieu (adieu). You should now be quite comfortable with grouping your <cfquery> output. 

If the code sample for the nested groups doesn?t make sense, or if you have any other questions or comments, shoot me an e-mail...or post a message in the forums. I look forward to hearing from you.

About This Tutorial
Author: Charlie Griefer (CJ)
Skill Level: Beginner 
 
 
 
Platforms Tested: CF5,CFMX
Total Views: 155,541
Submission Date: June 17, 2003
Last Update Date: June 05, 2009
All Tutorials By This Autor: 15
Discuss This Tutorial
  • How do you make the alternating colors for different rows of records in grouping? Let's say i want to hide book records, only show the author name records and has alternating colors for different author rows. Beside authorname field, i want to add other fields like author's birthday field and want to show it too. Thanks

  • Hu: just select that data in your query, and output it in your . for example: SELECT patient.patientID, patient.patientName, patient.gender, patient.dateofbirth, patient.clinicID, clinic.clinicID, clinic.clinicName FROM patient INNER JOIN clinic on patient.clinicID = clinic.clinkcID ORDER BY clinicName, patientName #clinicName#
    #patientName# - #gender# - #dateOfBirth#
    might need tweaking (obviously), but that's the gist.

  • I've been coding a while but didn't think to use the group attribute for a page I was creating. I'm glad I found this page because I couldn't figure out how to make the list I wanted to. It was exactly what I needed.

  • This is a good starting place for me as a newbie. What i'm trying to do is count how many times a presetnation id shows up in a sessions table and if the presentation id shows up more than one, I want a column to display a href to a pop up window. I have just about everything right except for the counting portion of the query... or the cfif statement. cf is goofy as I'm an asp dev by trade. Actually, maybe be email would be better. Thanks for your tutorial.

Advertisement

Sponsored By...
Mobile App Development (IOS, Android, Cordova, Phonegap, Objective-C, Java) - Austin, Texas Mobile Apps - Touch512, LLC.