Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL: How can I make sure all SELECT Cases are represented even if not all are executed?

Here is my code

SELECT
  CASE
       WHEN Money >= 20000 THEN '$ 20,000 + '
       WHEN Money BETWEEN 10000 AND 19999 THEN '$ 10,000 - $ 19,999'
       WHEN Money BETWEEN  5000 AND  9999 THEN '$  5,000 - $  9,999'
       WHEN Money BETWEEN     1 AND  4999 THEN '$      1 - $  4,999'
       ELSE '$      0'
   END AS [MONEY],
       COUNT(*) AS [#],
       MAX(Money) AS [MAX]
  FROM MyTable
 WHERE MoneyType = 'Type A'
 GROUP BY 
  CASE
       WHEN Money >= 20000 THEN '$ 20,000 + '
       WHEN Money BETWEEN 10000 AND 19999 THEN '$ 10,000 - $ 19,999'
       WHEN Money BETWEEN  5000 AND  9999 THEN '$  5,000 - $  9,999'
       WHEN Money BETWEEN     1 AND  4999 THEN '$      1 - $  4,999'
       ELSE '$      0'
   END
 ORDER BY MAX DESC 

Now my issue is I want all the cases to show a row in my result set but, since I dont have any values that would fall between 1 AND 4999 that row doesn't show up. I would still like that row to show up and just contain 0's for it's columns (except the first of course). Anyone can show me how to modify the code to accomplish this? Perhaps I need to do it a different way... Thanks!

Example of result set I'm looking for ...

  |  [MONEY]              |   [#]   |    [MAX]    |
  |  $ 20,000+            |   2     |    30,000   |
  |  $ 10,000 - $ 19,999  |   8     |    19,000   |
  |  $  5,000 - $  9,999  |   4     |     8,000   |
  |  $      1 - $  4,999  |   0     |     0       |     <-- Row currently doesn't show
  |  $      0             |   12    |     0       |
like image 855
daveomcd Avatar asked Dec 28 '22 00:12

daveomcd


2 Answers

You could build up a lookup table with a CTE and then use that to group on that instead of the Case statement. You'll need to make three other changes

  • The COUNT will need to be COUNT(t.Money) or you'll get a 1 when you're expecting a ZERO.
  • You may want to COALESCE your MAX(Money) but I'm not sure what you want it to be when its NULL
  • You can't actually order by MAX(MONEY) because it may be null. So its better to use the CTE to control the order as well


WITH Ranges AS
( SELECT 1 id , '$ 20,000 +'  description
  UNION SELECT 2 , '$ 10,000 - $19,999'
  UNION SELECT 3, '$  5,000 - $ 9,999'
  UNION SELECT 4, '$      1 - $ 4,999'
  UNION SELECT 5, '$      0')


SELECT
       r.Description as money,
       COUNT(t.Money) AS [#],
       MAX(Money) AS [MAX]
  FROM 
       Ranges r
       LEFT JOIN
        MyTable t
       ON r.ID =   CASE
                    WHEN Money >= 20000 THEN 1
                    WHEN Money BETWEEN 10000 AND 19999 THEN 2
                    WHEN Money BETWEEN  5000 AND  9999 THEN 3
                    WHEN Money BETWEEN     1 AND  4999 THEN 4
                    ELSE 5
                 END 
        AND  MoneyType = 'Type A' 
 GROUP BY 
  r.id,
  r.Description
 ORDER BY r.id asc

LIVE DEMO

like image 92
Conrad Frix Avatar answered Jan 14 '23 06:01

Conrad Frix


You could store the descriptive text in a real or ephemeral table & join accordingly, something like;

;with ranges(min,max,caption) as (
    select 10000, 19999, '$ 10,000 - $19,999' union
    select 5000, 9999,   '$  5,000 - $ 9,999' union
    select 1, 4999,      '$      1 - $ 4,999'
)
select
    isnull(r.caption, 'no description'),
    count(m.money) as [#],
    isnull(max(m.money), 0)
from
    mytable m
full outer join 
    ranges r on m.money between r.min and r.max
group by
    r.caption
like image 38
Alex K. Avatar answered Jan 14 '23 08:01

Alex K.