SQL: from WHERE to HAVING

June 22, 2010

WHERE filters results before they are grouped.
HAVING filters results after they are grouped.

correct:

 count players by team:
   SELECT  COUNT(*) count,  plheight_ft 
     FROM  ALplayers 
    WHERE  plheight_ft = 6
 GROUP BY  plheight_ft
 ORDER BY  count  

error:

   SELECT  COUNT(*) count,  plheight_ft 
     FROM  ALplayers 
    WHERE  count = 6      // <-------- error! 
 GROUP BY  plheight_ft
 ORDER BY  count  

correct:

   SELECT  COUNT(*) count,  plheight_ft 
     FROM  ALplayers 
 GROUP BY  plheight_ft
   HAVING  count = 6  
 ORDER BY  count  

we want to see if/how-many people have the same name:
(remove duplicate records)

1st, just look at a few:

   SELECT plplyrid, plNamef, plNamel, plpaddress
     FROM ALplayers
    WHERE plgameyear = '2006'
 ORDER BY plNamel, plNamef
    LIMIT 0, 44  

…there may be about 4 duplicates and about 10+ brothers/cousins

   SELECT  COUNT(*) as count, plplyrid, plNamef, plNamel, 
           concat(substring(plNamef, 1, 1), plNamel) as ckname,
           plpaddress
     FROM  ALplayers 
    WHERE  plgameyear = '2006'
 GROUP BY  ckname
   HAVING  count > 1  
 ORDER BY  ckname  

( — o.k. — I saw 7 dups.(2 each) )

now, to see those people:
1st way: join table to itself.

   SELECT  DISTINCT p1.plplyrkey, p1.plplyrid, p1.plNamef, p1.plNamel, 
           concat(substring(p1.plNamef, 1, 1), p1.plNamel) as ckname,
           p1.plpaddress, p1.plschlkey, p1.plheight_ft
     FROM  ALplayers as p1,  ALplayers as p2
    WHERE  p1.plgameyear = '2006'
      AND  p2.plgameyear = '2006'
      AND  concat(substring(p1.plNamef, 1, 1), p1.plNamel) = 
           concat(substring(p2.plNamef, 1, 1), p2.plNamel)
      AND  p1.plplyrid != p2.plplyrid  
 ORDER BY  ckname   

— ahha, 3 of the 7 are brothers or cousins with the same 1st initial.

now, to see those people again:
2nd way: sub-query. (works in mysql 5.x)

   SELECT  p1.plplyrkey, p1.plplyrid, p1.plNamef, p1.plNamel, 
           concat(substring(p1.plNamef, 1, 1), p1.plNamel) as ckname,
           p1.plpaddress, p1.plschlkey, p1.plheight_ft
     FROM  ALplayers as p1
    WHERE  p1.plgameyear = '2006'
    HAVING  ckname  IN
          (SELECT  concat(substring(p2.plNamef, 1, 1), p2.plNamel) as p2ckname
             FROM  ALplayers as p2
            WHERE  p2.plgameyear = '2006'
         GROUP BY  p2ckname
           HAVING  count(*) > 1 ) 
   ORDER BY  ckname   

# display duplicate records, if there are any (only works in mysql 5.x) :

    SELECT  h1.orderid
      FROM  oltohead as h1
    HAVING  orderid  IN
          (SELECT  h2.orderid
             FROM  oltohead as h2
         GROUP BY  h2.orderid
           HAVING  count(*) > 1 ) 
  ORDER BY  h1.orderid   

To find the customers with total orders of less than 100.

   SELECT  Customer,  SUM(OrderPrice)  
     FROM  Orders
 GROUP BY  Customer
   HAVING  SUM(OrderPrice) < 100   

To find if the customers "Jones" or "Smith" have total orders of more than 300.

    SELECT  Customer,  SUM(OrderPrice) 
      FROM  Orders
     WHERE  Customer='Jones'
        OR  Customer='Smith'
  GROUP BY  Customer
    HAVING  SUM(OrderPrice) > 300   

The HAVING clause can refer to aggregate functions, which the WHERE clause cannot:

     SELECT  user, MAX(salary) 
       FROM  users
   GROUP BY  user 
     HAVING  MAX(salary) > 10;  

To find duplicates on a field that hasn't been uniquely indexed, you can do this:

   SELECT  BookISBN, count(BookISBN) 
     FROM  Books 
 GROUP BY  BookISBN HAVING COUNT(BookISBN) > 1;   

Leave a Reply

We try to post all comments within 1 business day