トップページに戻る    次のSQLパズルへ    前のSQLパズルへ

2-3-6 メジアン(中央値)を取得

SQLパズル

中央値テーブル
Col1
----
   1
   5
  15
  20

中央値テーブルのCol1の中央値を取得する。

出力結果
中央値
------
    10


データ作成スクリプト

create table 中央値テーブル(Col1 number(2));

insert into 中央値テーブル values(1);
insert into 中央値テーブル values(5);
insert into 中央値テーブル values(15);
insert into 中央値テーブル values(20);
commit;


SQL

--■■■分析関数を使う方法■■■
select avg(Col1) as 中央値
from (select count(*) over() as RecordCount,
      Col1,
      Row_Number() over(order by Col1) as Rank
      from 中央値テーブル)
where (mod(RecordCount,2) = 0 and Rank in(RecordCount/2,RecordCount/2+1))
   or (mod(RecordCount,2) = 1 and Rank = Ceil(RecordCount/2));

--■■■Median関数を使う方法(10g以降)■■■
select Median(Col1) as 中央値
  from 中央値テーブル;

--■■■percentile_cont関数を使う方法■■■
select percentile_cont(0.5) within group(order by Col1) as 中央値
  from 中央値テーブル;

--■■■逆ソートを使う方法(分析関数版)■■■
select avg(Col1) as 中央値
from (select Col1,
      Row_Number() over(order by Col1 asc ,RowID asc ) as Rank,
      Row_Number() over(order by Col1 desc,RowID desc) as RevRank
        from 中央値テーブル)
where abs(Rank - RevRank) <= 1;

--■■■逆ソートを使う方法(相関サブクエリ版)■■■
select avg(Col1) as 中央値
  from 中央値テーブル a
 where (select
          abs(sum(case when a.Col1  < b.Col1
                         or (a.Col1 = b.Col1 and a.RowID <= b.RowID)
                       then 1 else 0 end)
            - sum(case when a.Col1  > b.Col1
                         or (a.Col1 = b.Col1 and a.RowID >= b.RowID)
                       then 1 else 0 end))
          from 中央値テーブル b) <= 1;

--■■■正順位 + 逆順位 = 件数 + 1 を移項した式を使う方法(分析関数を使用)■■■
select avg(Col1) as 中央値
from (select Col1,
      Row_Number() over(order by Col1) as Rank,
      count(*) over() as RecordCount
        from 中央値テーブル)
where abs(2*Rank - RecordCount -1) <= 1;

--■■■正順位 + 逆順位 = 件数 + 1 を移項した式を使う方法(分析関数を使用せず)■■■
select avg(a.Col1) as 中央値
  from (select Col1,RowNum as Rank
          from (select Col1 from 中央値テーブル
                order by Col1)) a,
       (select count(*) as RecordCount from 中央値テーブル) b
where abs(2*Rank - RecordCount -1) <= 1;


解説

分析関数を使う方法では、
件数が偶数の場合と、奇数の場合とで、
対象となるレコードを分岐させています。

■■■■■■■■■■■■■■■■■■■■■■■■■■■
逆ソートを使う方法では、この図をイメージしてます。

偶数の場合
Col1  Rank  RevRank  abs(Rank-RevRank)
----  ----  -------  -----------------
  10     1        6     5
  10     2        5     3
  20     3        4     1
  20     4        3     1
  30     5        2     3
  30     6        1     5

奇数の場合
Col1  Rank  RevRank  abs(Rank-RevRank)
----  ----  -------  -----------------
  10     1        5     4
  10     2        4     2
  20     3        3     0
  20     4        2     2
  30     5        1     4

■■■■■■■■■■■■■■■■■■■■■■■■■■■
正順位 + 逆順位 = 件数 + 1
移項して
逆順位 = 件数 + 1 - 正順位

よって
正順位 - 逆順位
=正順位 - (件数 + 1 - 正順位)
= 2*正順位 - 件数 - 1

を使う方法もあります。

マニュアル(Median関数)(英語)
マニュアル(Median関数)

CodeZine:HAVING句の力
CodeZine:分析関数の衝撃(前編)

2-3-30 グループごとのメジアン(中央値)を取得
2-3-31 標準SQLでメジアン(中央値)を取得