You might employ case statement to get counts of exclusive ranges:
select case when [vendor experience] <= 6 then '0-6'
when [vendor experience] <= 12 then '0-12'
when [vendor experience] <= 18 then '0-18'
else 'more'
end [vendor_experience(months)],
count (*) [count]
from experiences
group by
case when [vendor experience] <= 6 then '0-6'
when [vendor experience] <= 12 then '0-12'
when [vendor experience] <= 18 then '0-18'
else 'more'
end
This produces the same result as yours (inclusive ranges):
; with ranges as
(
select 6 as val, 0 as count_all
union all
select 12, 0
union all
select 18, 0
union all
select 0, 1
)
select case when ranges.count_all = 1
then 'more'
else '0-' + convert (varchar(10), ranges.val)
end [vendor_experience(months)],
sum (case when ranges.count_all = 1
or experiences.[vendor experience] <= ranges.val
then 1 end) [count]
from experiences
cross join ranges
group by ranges.val, ranges.count_all
count_all
is set to 1 to mark open-ending range.
Sql Fiddle is here.
UPDATE: an attempt at explanation.
The first part starting with with
and ending with closing bracket is called CTE. Sometimes it is referred to as inline view
because it can be used multiple times in the same query and under some circumstances is updateable. Here it is used to prepare data for ranges and is appropriately named ranges
. This name one uses in main query. Val
is maximum value of a range, count_all
is 1 if range has no upper end (18+, more, or however you wish to call it). Data rows are combined by means of union all. You might copy/paste section between parenthesis only and run it just to see the results.
Main body joins experiences
table with ranges using cross join. This creates combinations of all rows from experiences
and ranges
. For row d 11
there will be 4 rows,
empname vendor experience val count_all
d 11 6 0
d 11 12 0
d 11 18 0
d 11 0 1
First case statement in select list produces caption by checking count_all - if it is one, outputs more
, else constructs caption using upper range value. Second case statement counts using sum(1). As aggregate functions ignore nulls, and case having no else evaluates to null if match was not found, it is sufficient to check if count_all is true (meaning that this row from experiences is counted in this range) or if vendor experience
is less or equal to upper range value of current range. In example above 11 will not be counted for first range but will be counted for all the rest.
Results are then grouped by val and count_all. To better see how it works you might remove group by and sum() and look at numbers before aggregation. Order by empname, val will help to see how values of [count] change depending on different val per an employee.
Note: I did my best with my current level of english language. Please don't hesitate to ask for clarification if you need one (or two, or as many as you need).