SQL Aggregates Support CASE Statements

Posted March 15, 2007 at 12:05 PM

Daniel Roberts, Tony Petruzzi, and Rick Osborne just taught me some revolutionary stuff in SQL. Unbeknownst to me, SQL aggregates (ex. SUM, COUNT) can handle CASE statements as part of their execution logic. This is HUGE! I can't believe I didn't know this (or rather, I can easily believe it, but I am saddened).

So just quickly, as this post is more about excitement than it is about teaching, let me just show you how I might rework my previous SQL with new CASE statements:

 Launch code in new window » Download code as text file »

  • SELECT
  • g.hair,
  • (
  • COUNT( * )
  • ) AS girl_count,
  • (
  • COUNT(
  • CASE
  • WHEN did_date = 1
  • THEN did_date
  • ELSE NULL
  • END
  • )
  • ) AS did_date_count,
  • (
  • COUNT(
  • CASE
  • WHEN did_date = 0
  • THEN did_date
  • ELSE NULL
  • END
  • )
  • ) AS did_not_date_count
  • FROM
  • @girl g
  • GROUP BY
  • g.hair

Thanks guys, for taking the time to teach some stuff :)

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page


You Might Also Be Interested In:



Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

Reader Comments

Mar 15, 2007 at 12:55 PM // reply »
27 Comments

Just want to point out that per record functions can be quite taxing for large queries versus other solutions.

I worked on a reporting site that originally ran from a log table in an Access database which was later moved to MSSQL. There was summing on t/f fields to come up with some counts and since Access t/f values are -1 the absolute value was found for summing. It looked like this: Sum(Abs(field)). Notice anything? Well the functions are improperly nested for this need. Just switching around the nesting to Abs(Sum(field)) greatly improvement execution time. That is 1 Abs() call versus N records of calls.

Queries on a well indexed database may perform better using subqueries to get counts and such versus aggregates on case statements. Just one more thing to think about :-)


Mar 15, 2007 at 1:01 PM // reply »
5 Comments

You can actually make it simpler. because you are just counting, you can use a 1 instead of the field for the value to count, and if you want a null, you can just leave out the else:

# COUNT(
# CASE
# WHEN did_date = 0
# THEN 1
# END
# )


Mar 15, 2007 at 1:04 PM // reply »
6,516 Comments

@Dan,

That is a good point. I think the kind of situations where I would use stuff like this are general less than optimal, so every little tip can help.


Mar 15, 2007 at 1:05 PM // reply »
6,516 Comments

@Ryan,

This is true. Daniel eluded to something very similar in my earlier blog post (that prompted this one). Sometimes it's just a matter of standing back, taking stock of the situation, and realizing that there is more than one way to attack a problem.

... my part is the "realization" step :)


Mar 15, 2007 at 1:06 PM // reply »
6,516 Comments

*my "problem" is with the realization step :)


Mar 15, 2007 at 2:13 PM // reply »
153 Comments

As an aside, there's a second form of CASE that you don't see much, but you and your curious nature might want to play around with:

CASE
WHEN foo = 'A' THEN 1
WHEN foo = 'B' THEN 2
END

- versus -

CASE foo
WHEN 'A' THEN 1
WHEN 'B' THEN 2
END

I know that, on some systems, the latter case will speed up the query a hair. I think SQL Server is smart enough that it doesn't matter, but I remember rewriting a bunch of queries on the old version of DB2 we had here.

And also keep in mind that if you don't have an ELSE part, the CASE returns NULL. This may seem like a "duh", but think about how that affects aggregate functions:

SELECT
AVG(CASE WHEN a > 0 THEN a ELSE 0 END) AS WithZeros,
AVG(CASE WHEN a > 0 THEN a END) AS WithNulls
FROM (
SELECT 4 AS a UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT -1 UNION ALL
SELECT -2 UNION ALL
SELECT -3
) AS b

This returns: WithZeros = 2, WithNulls = 5

The AVG function doesn't treat NULLS as values - it basically pretends they aren't in the set. Thus, the WithZeros AVG uses a set of (4,5,6,0,0,0) for an average of 2, while the WithNulls AVG uses a set of (4,5,6,NULL,NULL,NULL), which collapses to (4,5,6) and thus averages out to 5.

Mean what you say and say what you mean.


Mar 15, 2007 at 4:51 PM // reply »
6,516 Comments

@Rick,

Very cool. And this is awesome:

FROM (
SELECT 4 AS a UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT -1 UNION ALL
SELECT -2 UNION ALL
SELECT -3
) AS b

I have never seen anything like that before. That could be pretty useful at times. All good stuff!


Mar 15, 2007 at 6:10 PM // reply »
153 Comments

Heh. Use with care. Not many DBMSes like that syntax and it is normally not optimized at all. SQL Server, obviously, is okay with it. DB2 barfs. mySQL v5 is okay with it. Access complains about the lack of tables.

I'll generally use the pivot table trick we've discussed previously, but in a pinch when you're not set up for one, a UNIONed subselect will do if the DBMS is okay with it.


Mar 16, 2007 at 7:27 AM // reply »
6,516 Comments

Oh trust me, I make good use of the pivot table. I use it for pretty much anything that involves a number of months (a lot of the reporting I do). It has made my life much easier. You are a rock star (even if you have trouble with daylight savings ;) ).


Post Comment  |  Ask Ben

Recent Blog Comments
Nov 21, 2009 at 5:15 PM
Using ColdFusion Structures To Remove Duplicate List Values
@Jose Galdamez, Oh heh yeah I didn't paste the whole code. I should have defined the vars -- my bad. It's fixed thou. Thanks. ... read »
Nov 21, 2009 at 4:49 PM
Styling The ColdFusion 8 WriteToBrowser CFImage Output
Great work yet again Ben! Whilst I didn't use this whole code, I copied some of your regex code for a similar problem with the lack of an alt attribute and unescaped ampersands in CFIMAGE for Railo 3 ... read »
Nov 21, 2009 at 1:13 PM
My First ColdFusion Builder Extension - Encrypting And Decrypting CFM / CFC Files
@Ben, Because I am pedantic, I just want to make sure that everyone knows there is absolutely no encryption going on. There is only encoding and obfuscation. The cfencode tool only obfuscates your C ... read »
Nov 21, 2009 at 12:28 PM
Using ColdFusion Structures To Remove Duplicate List Values
@Jody I can't seem to get your code sample to work. If you are still having problems, try this code out and see if it gets you what you wanted. <!--- Comma delimited list with various duplicates ... read »
Nov 21, 2009 at 11:03 AM
Groovy Operator Overloading Does Not Work In The ColdFusion Context
Hi Ben, Thanks for this informative post. Now I am reading ur old posts too ... read »
Nov 21, 2009 at 10:56 AM
HostMySite.com Has The Best ColdFusion Hosting
@Mehul, Yes very nice people, however several downtimes per day which was not acceptable. Hence we had to move out. I am glad you are having good luck with them so far. ... read »
Nov 20, 2009 at 11:32 PM
Five Months Without Hungarian Notation And I'm Loving It
I've used headless camel case for years for not only ColdFusion variables, but also SQL tables and fields... pretty much everything involving code. I also subscribe to the "don't abbreviate and clea ... read »