SQLのHAVING句はSELECT内CASE WHENで置き換えられる

#HAVINGとCASE WHEN
 インターン先でSQLを書き始めて３週間ほどが経ちました。「スッキリ分かるSQL入門」をさらっと見た後（ドリルはやっていない）、有名な「達人に学ぶSQL徹底指南書」を読み始めました。
 本書ではCASE WHEN句と自己結合、HAVING句やEXISTを特に推して説明しているのですが、読んでいる内に少し違和感のある文を見つけました。
　それはCASE WHENを説明している章で
>HAVING句で条件分岐させるのは素人のやること。プロはSELECT句で分岐させる

と書いているのです。
 他ではHAVING句（２章分も）推しているのに、それをやるのは「素人」だと言っているのです。
私はちょっとイライラしたので、とりあえずHAVING句で説明している一章分を、CASE WHENで書き直しながら確認してみることにしました。

#サンプルデータ
>http://www.geocities.jp/mickindex/database/db_support_sinan.html#LocalLink-p1_4
に筆者が提供しているデータがサンプルデータの挿入コードがあります。今回試したのはその中の、「1-4．HAVING句の力」のデータです。

```
CREATE TABLE Graduates
(name   VARCHAR(16) PRIMARY KEY,
 income INTEGER NOT NULL);

INSERT INTO Graduates VALUES('サンプソン', 400000);
INSERT INTO Graduates VALUES('マイク',     30000);
INSERT INTO Graduates VALUES('ホワイト',   20000);
INSERT INTO Graduates VALUES('アーノルド', 20000);
INSERT INTO Graduates VALUES('スミス',     20000);
INSERT INTO Graduates VALUES('ロレンス',   15000);
INSERT INTO Graduates VALUES('ハドソン',   15000);
INSERT INTO Graduates VALUES('ケント',     10000);
INSERT INTO Graduates VALUES('ベッカー',   10000);
INSERT INTO Graduates VALUES('スコット',   10000);

```

#サンプル(最頻値と中央値を求める)

| name       |       income | 
|:-----------------|------------------:|
| サンプソン             |              400,000 |
| マイク           |            30,000|
| ホワイト             |              20,000 |
| アーノルド               |                20,000 |
| スミス             |             20,000 |
| ロレンス          |           15,000 |
| ハドソン          |           15,000 |
| ケント          |           10,000 |
| ベッカー          |           10,000 |
| スコット          |           10,000 |

このデータから最頻値を求めます。 上がHAVINGで下がCASEです。

```postgres:having.sql

SELECT income
FROM Graduates
GROUP BY income
HAVING COUNT(*) >= ALL ( SELECT COUNT(*)
                             FROM Graduates
                         GROUP BY income);
```

```text:having.sqlの結果
income 
--------
  10000
  20000
```

```postgres:case.sql

select
income,
case when 
     count(*) >= all(select count(*) from graduates group by income) then count(*) else null end
from Graduates
group by income
```

```text:case.sqlの結果
 income | case 
--------+------
  30000 |     
 400000 |     
  10000 |    3
  20000 |    3
  15000 |     
```

caseでnull値を除外する方法が思いつきませんでした。もう１階層またぐことなく、null値を除外して表示する関数とか、方法を教えていただけたらうれしいです。でも該当箇所以外の情報も得られるから、これはこれで良いのかもしれない。

次に中央値を求めます。

```postgres:having.sql

SELECT AVG(DISTINCT income)
  FROM (SELECT T1.income
          FROM Graduates T1, Graduates T2
      GROUP BY T1.income
        HAVING SUM(CASE WHEN T2.income >= T1.income THEN 1 ELSE 0 END) 
                   >= COUNT(*) / 2
           AND SUM(CASE WHEN T2.income <= T1.income THEN 1 ELSE 0 END) 
                   >= COUNT(*) / 2 ) TMP;
```

```text:having.sqlの結果
        avg         
--------------------
 17500.000000000000
```


```postgres:case.sql

elect AVG(newIncome)
from(
		SELECT
			CASE WHEN 
				SUM(case when t2.income >= t1.income then 1 else 0 end) >= count(*) / 2
			and SUM(case when t2.income <= t1.income then 1 else 0 end) >= count(*) / 2
			THEN t1.income
			else null
			end as newIncome

		from graduates t1, graduates t2
		group by t1.income
	) tmp
```

```text:case.sqlの結果
        avg         
--------------------
 17500.000000000000
```


#まとめ
 確かにHAVINGで行える処理は、丸々同じ条件をSELECT内CASE WHEN句に書くことによって代替できそうですね。他のサンプルもいじってみましたが、殆ど同じ方法で代替可能でした。

 とすると、どちらの方法で行うほうが良いのか、素人の私にはイマイチよく分からなくなってきた今日このごろです。なんとなくHAVINGで処理したほうが可読性は良い気がするので、最初はHAVINGで書こうと思いますが。

 GROUP BYでグループ分けした後は、とりあえずHAVINGで処理しようとしたほうが好ましいのですかね？　どなたかコメントしていただけると嬉しいです。