To get the final result that you want you can use the PIVOT function. I would first start with a subquery that returns all of your data plus gives you a total of each activity per location:
select l.name location,
c.name category,
count(b.locationid) over(partition by b.locationid) total
from location l
left join business b
on l.id = b.locationid
left join category c
on b.categoryid = c.id;
See SQL Fiddle with Demo. Using the windowing function count() over()
creates the total number of activities for each location. Once you have this, then you can pivot the data to convert your categories to columns:
select
isnull(location, 'Total') Location,
sum([Activities]) Activities,
sum([Bars]) bars,
sum([Sweet Shops]) SweetShops,
sum(tot) total
from
(
select l.name location,
c.name category,
count(b.locationid) over(partition by b.locationid) tot
from location l
left join business b
on l.id = b.locationid
left join category c
on b.categoryid = c.id
) d
pivot
(
count(category)
for category in ([Activities], [Bars], [Sweet Shops])
) piv
group by grouping sets(location, ());
See SQL Fiddle with Demo. I also implemented GROUPING SETS()
to create the final row with the totals for each activity.
The above works great if you have a limited number of activities but if your activities will be unknown, then you will want to use dynamic SQL:
DECLARE
@cols AS NVARCHAR(MAX),
@colsgroup AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT ',' + QUOTENAME(name)
from dbo.category
group by id, name
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select @colsgroup = STUFF((SELECT ', sum(' + QUOTENAME(name)+ ') as '+ QUOTENAME(name)
from dbo.category
group by id, name
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = N'SELECT
Isnull(location, ''Total'') Location, '+ @colsgroup + ', sum(Total) as Total
from
(
select l.name location,
c.name category,
count(b.locationid) over(partition by b.locationid) total
from location l
left join business b
on l.id = b.locationid
left join category c
on b.categoryid = c.id
) x
pivot
(
count(category)
for category in ('+@cols+')
) p
group by grouping sets(location, ());'
exec sp_executesql @query;
See SQL Fiddle with Demo. Both versions give the result:
| LOCATION | ACTIVITIES | BARS | SWEET SHOPS | TOTAL |
|-----------|------------|------|-------------|-------|
| Chester | 1 | 0 | 0 | 1 |
| Frodsham | 0 | 1 | 0 | 1 |
| Stockport | 1 | 0 | 1 | 2 |
| Total | 2 | 1 | 1 | 4 |