Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 General SQL Server Forums
 New to SQL Server Programming
 How do you return a 0 count for an empty set?

Author  Topic 

Aleph_0
Yak Posting Veteran

79 Posts

Posted - 2012-07-24 : 12:33:07
Hi there, it's been a while since I've been stuck on a problem - thanks for all the help in the past! Here's what I'm trying to do:


IF OBJECT_ID('tempdb..#AlephTest') IS NOT NULL
DROP TABLE #AlephTest

SELECT
[location_id] = 1
, [customer_id] = 12345
, [age] = 10
INTO #AlephTest
UNION ALL SELECT 1, 21752, 28
UNION ALL SELECT 1, 98712, 25
UNION ALL SELECT 1, 12896, 23
UNION ALL SELECT 2, 12874, 38
UNION ALL SELECT 2, 23987, 29
UNION ALL SELECT 2, 23876, 23
UNION ALL SELECT 2, 54961, 24

SELECT
a.location_id
, [age_group] = CASE
WHEN a.age < 18 THEN '<18'
WHEN a.age BETWEEN 18 AND 35 THEN '18 to 35'
WHEN a.age > 35 THEN '>35'
END
, [customer_count] = Count(*)
FROM #AlephTest a
GROUP BY
a.location_id
, CASE
WHEN a.age < 18 THEN '<18'
WHEN a.age BETWEEN 18 AND 35 THEN '18 to 35'
WHEN a.age > 35 THEN '>35'
END
ORDER BY
a.location_id
, CASE (CASE
WHEN a.age < 18 THEN '<18'
WHEN a.age BETWEEN 18 AND 35 THEN '18 to 35'
WHEN a.age > 35 THEN '>35'
END)
WHEN '<18' THEN 1
WHEN '18 to 35' THEN 2
ELSE 3
END


I'd like all 3 age groups to show for each location, and if there aren't any customers in an age group it should return 0 as the customer count. Is my error in the GROUP BY? I tried putting the Count(*) in a COALESCE(Count(*), 0), but that didn't work.

Also, since I'm not an IT guy, would you mind also showing me how a real developer would format my code?

Thanks!

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2012-07-24 : 12:59:31
you will need a separate range table like below

CREATE TABLE #AgeRange
(
RangeDesc varchar(20),
AgeStart int,
AgeEnd
)

INSERT #AgeRange
SELECT '<18',1,17 UNION ALL
SELECT '18 to 35',18,35 UNION ALL
SELECT '>35',36,120

then use it like

SELECT
m.location_id,m.RagenDesc,
COUNT(n.customer_id) AS Count
FROM
(
SELECT t.location_id,r.RangeDesc,r.AgeStart,r.AgeEnd
FROM (SELECT DISTINCT location_id FROM #Alephtest) t
CROSS JOIN #AgeRange r
)m
LEFT JOIN #Alephtest n
ON n.location_id = m.location_id
AND n.age BETWEEN m.AgeStart AND m.AgeEnd
GROUP BY m.location_id,m.RangeDesc


------------------------------------------------------------------------------------------------------
SQL Server MVP
http://visakhm.blogspot.com/

Go to Top of Page

nigelrivett
Master Smack Fu Yak Hacker

3385 Posts

Posted - 2012-07-24 : 13:23:07
;with cte1 as
(
select Startage = 0, endage = 17, description = '<18', seq = 1
union all
select Startage = 18, endage = 35, description = '18 to 35', seq = 2
union all
select Startage = 36, endage = 1000, description = '>35', seq = 3
) ,
cte as
(
select * from cte1
cross join (select distinct location_id from #AlephTest) t
)
SELECT
cte.location_id
, [age_group] = cte.description
, [customer_count] = SUM(case when a.location_id is null then 0 else 1 end)
FROM cte
left join #AlephTest a
on a.age between cte.Startage and cte.endage
and a.location_id = cte.location_id
GROUP BY
cte.location_id , cte.description, cte.seq
ORDER BY
cte.location_id ,
cte.seq


==========================================
Cursors are useful if you don't know sql.
SSIS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

Aleph_0
Yak Posting Veteran

79 Posts

Posted - 2012-07-24 : 13:39:05
Thanks, both of you! I had a feeling I had to create another table, but I had completely forgotten about the CROSS JOIN. You guys are awesome!
Go to Top of Page

nigelrivett
Master Smack Fu Yak Hacker

3385 Posts

Posted - 2012-07-24 : 13:45:45
To be fair - my post is farly similar to Visakh16's. I wouldn't have posted if I'd read his more carefully. The only thing it adds really is the sequence for the ranges.

==========================================
Cursors are useful if you don't know sql.
SSIS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

Aleph_0
Yak Posting Veteran

79 Posts

Posted - 2012-07-24 : 13:48:38
I noticed - I took that as confirmation that it was a good approach. Not to mention that is just plain made sense. :)
Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2012-07-24 : 14:09:19
If this is something you do frequently report on and if you really want to make solution more scalable then i would suggest making AgeRange as a permanent table. The advantage is anytime you want to regroup your age ranges or add or modify range you can easily do it by means of DML operation to table. Your procedure/sql code will not require any modifications at all.

------------------------------------------------------------------------------------------------------
SQL Server MVP
http://visakhm.blogspot.com/

Go to Top of Page
   

- Advertisement -