Kobarin's Development Blog

C#やASP.NET、公開APIなどについての記録です。

SQLserverで、度数テーブルを使わずに度数分布

「俺の知合い」テーブル
人名 体重
たけし 60.5
つよし 55.8
まさし 71.2
・・・ ・・

のようなテーブルがあり、これを度数分布表示行おうとした場合、
以下のようなテーブルを作り、SQLのbetweenを使って表示する手法がある。

「体重ランク」テーブル
下限 上限
0 39
40 49
50 59
60 69
70 79
80 99
100 999

SQL

select 体重.下限. 体重.上限, count(*)
 from 俺の知合い, 体重ランク
 where 俺の知合い.体重 between 体重ランク.下限 and 体重ランク.上限
 order by 1

この手法だと、段階数が多ければ有効である一方、たかだか数段階程度の場合でもわざわざ体重ランクのテーブルを作成してやる必要がある。
そこで、unionを使って以下のように行う事で代用可能だ。

unionを使った代替方法

select 体重.下限. 体重.上限, count(*) as 人数
 from 俺の知合い,
  (select 0,39 union select 40,49 union select 50,59 union select 60,69 union ...) as  体重ランク
 where 俺の知合い.体重 between 体重ランク.下限 and 体重ランク.上限
 order by 1
結果
下限 上限 人数
40 49 3
60 69 15
70 79 8

新たな課題

さて上記表を見て気付く人もいると思うが、上記のような方法だと、各ランクに該当する体重の人間がいない場合はランク自体が実行結果に表れない。
つまり上記表で言うと、

  • 0kg〜39kb
  • 50kg〜59kg
  • 80kg〜99kg
  • 100kg〜

のランクが現れないのだ。度数分布を表す場合、これではちょっと具合が悪いだろう。
その場合の改善版SQL文は以下の通り。

全てのランクを表示させるSQL

select 体重.下限. 体重.上限, count(*) as 人数
 from 俺の知合い right outer join
  (select 0,39 union select 40,49 union select 50,59 union select 60,69 union ...) as  体重ランク
  on 俺の知合い.体重 between 体重ランク.下限 and 体重ランク.上限
 order by 1

つまり、右結合を行えばよいのだ。実行結果は下記の通り。

下限 上限 人数
0 39 0
40 49 3
50 59 0
60 69 15
70 79 8
80 99 0
100 999 0