Rob Verschoor posted my new User Defined Function sp_range() on his web site:

The collection of useful SQL UDFs has been extended with 'sp_range()',
a function provided by Robert Munson. This can be used in 'group by'
and 'order by' to get a statistical distribution of unknown data (see
the comments in the SQL UDF code for more info and examples).
http://www.sypron. nl/udf
(remove the space in url)

Here's sp_range() used to get a distribution of the size of tables (in MB) in a database. I've replaced spaces with periods in the output to be certain the spacing turns out right.

1> select dbo.sp_range(cast(pagecnt /512.0 as int)) as "MB Range",
2> count(*) as "Tables",
3> convert(numeric(10,2),sum(pagecnt)/512.0) as "Megs",
4> sum(cast(rowcnt as int)) as "Total Rows"
5> from systabstats
6> where indid <=1 and id>100
7> group by dbo.sp_range(cast(pagecnt/512.0 as int))
8> order by dbo.sp_range(cast(pagecnt/512.0 as int))
9> go
.MB.Range.................................Tables.. ....Megs..........Total.Rows
.----------------------------------------.-----------.-------------.-----------
...........0-0....................................685.........3 0.55......476214
...........1-1.....................................10.........1 4.71......242424
...........2-4.....................................26.........8 5.67.....1041552
...........5-9......................................9.........6 2.60......868566
..........10-19.....................................5.........7 7.10.....1626937
..........20-49.....................................7........23 0.45.....2670970
..........50-99....................................16.......118 0.08....19487036
.........100-199...................................17.......247 0.39....30280934
.........200-499...................................31......1026 3.55...115533312
.........500-999...................................17......1187 2.75...115689642
........1000-1999..................................14......1998 1.78...136043603
........2000-4999..................................20......6506 5.61...378484041
........5000-9999...................................4......2388 7.99...162588149
.......10000-19999..................................3......4175 6.74...370772937
.......20000-49999..................................4.....13393 7.11...525323957


You can try the following to get a distribution of table size by number of rows:
1> select dbo.sp_range(cast(rowcnt as int)) as "Row Range",
2> count(*) as "Tables",
3> convert(numeric(10,2),sum(pagecnt)/512.0) as "Megs",
4> sum(cast(rowcnt as int)) as "Total Rows"
5> from systabstats
6> where indid <=1 and id>100
7> group by dbo.sp_range(cast(rowcnt as int))
8> order by dbo.sp_range(cast(rowcnt as int))
9> go

And finally, here's a sample with one of the monitoring tables:

1> select dbo.sp_range(LockWaits) as "Range",
2> count(*) as "Count" -- Note: LockWaits is # times, not elapsed time waiting.
3> from master..monOpenObjectActivity
4> where LockWaits is not null and LockWaits >= 0
5> group by dbo.sp_range(LockWaits)
6> order by dbo.sp_range(LockWaits)
7> go

Range....................................Count

----------------------------------------.-----------
...........0-0....................................879
...........1-1.....................................10
...........2-4......................................8
...........5-9.....................................14
..........10-19....................................15
..........20-49.....................................5
..........50-99.....................................1
.........100-199....................................1
........5000-9999...................................1
.......50000-99999..................................2


The argument to sp_range() is a non negative integer, which is why you need to use cast() and exclude negatives and nulls in the query. I've used system tables for the examples, but sp_range() is certainly not limited to that.