メジアンを求める ~世界の中心を目指せ~
テーブルの行に連番を振れるようになれば、
例題として、
生徒の体重を表すテーブルがあるとします。メジアンの計算はデータ数が奇数と偶数の場合で分かれるので、
student_ | Weight |
---|---|
A100 | 50 |
A101 | 55 |
A124 | 55 |
B343 | 60 |
B346 | 72 |
C563 | 72 |
C345 | 72 |
student_ | Weight |
---|---|
A100 | 50 |
A101 | 55 |
A124 | 55 |
B343 | 60 |
B346 | 72 |
C563 | 72 |
C345 | 72 |
C478 | 90 |
集合指向的な解法
昔から知られている集合指向的な解法では、
SELECT AVG(weight)
FROM (SELECT W1.weight
FROM Weights W1, Weights W2
GROUP BY W1.weight
--S1(下位集合)の条件
HAVING SUM(CASE WHEN W2.weight >= W1.weight THEN 1 ELSE 0 END)
>= COUNT(*) / 2
--S2(上位集合)の条件
AND SUM(CASE WHEN W2.weight <= W1.weight THEN 1 ELSE 0 END)
>= COUNT(*) / 2 ) TMP;
![図4 集合指向的な解法のイメージ図 図4 集合指向的な解法のイメージ図](/assets/images/dev/serial/01/sql_academy2/000103/001.png)
手続き型の解法
一方、
SELECT AVG(weight) AS median
FROM (SELECT weight,
ROW_NUMBER() OVER (ORDER BY weight ASC, student_id ASC) AS hi,
ROW_NUMBER() OVER (ORDER BY weight DESC, student_id DESC) AS lo
FROM Weights) TMP
WHERE hi IN (lo, lo +1 , lo -1);
![図5 手続き型の解法のイメージ図 図5 手続き型の解法のイメージ図](/assets/images/dev/serial/01/sql_academy2/000103/thumb/TH800_002.png)
奇数の場合は、
この解法において、
演習問題
集合指向と手続き型、
- 問題1
- リスト10の手続き的なクエリを、
実装非依存で動くようROW_ NUMBERを使わずに書き換えてください。 - 問題2
- リスト10の手続き的なクエリでは、
ソートキーに体重(weight) 列のほかに主キーの学生ID(student_ id)を含んでいます。このキーを除外すると、 このクエリは正しく動作しません。一体なぜでしょう? - 問題3
- 問題1で使った、
クラスごとに分割されたテーブル (Weights2) から、 クラス単位にメジアンを求めるコードを考えます。クラス1は55kg、 クラス2は72. 5kg。これらを一度に求められるよう、 リスト9と10の集合指向のクエリと手続き型のクエリをともに拡張してください。