こんにちは、knsk765 です。
こちらの記事の続きです。 blog.ingage.jp
前編では1940年〜2039年全日付の誕生数を SQL で導き出しました。
後編では集計処理に取り組みます。
おさらい
こんなデータを取得するところまではできました。
d | birth_number |
---|---|
1940-01-01 | 7 |
1940-01-02 | 8 |
1940-01-03 | 9 |
1940-01-04 | 1 |
1940-01-05 | 2 |
1940-01-06 | 3 |
1940-01-07 | 22 |
1940-01-08 | 5 |
1940-01-09 | 6 |
1940-01-10 | 7 |
: | : |
ゴールに至るまでの手順
- 1940年〜2039年までの日付を並べる
- 各日付に対する誕生数を割り出す ← ここまでクリア
- 20年ごとに分けて各誕生数の割合を算出する
今回やること
Step 5. 20年単位での集計処理
1940年から20年単位にわけて集計します。誕生数で GROUP BY
した結果を 20年単位の列に分けて表示します。
WITH RECURSIVE dates_core (d) AS ( SELECT cast('1940-01-01' as date) AS d UNION ALL SELECT d + 1 AS d FROM dates_core WHERE d < '2039-12-31' ), -- YYYYMMDD の日付文字列を1文字ずつ分解して和を求めます dates (d, added_digits) AS ( SELECT d, ( SELECT sum(cast(i as smallint)) FROM regexp_split_to_table(to_char(d, 'YYYYMMDD'), '') AS i ) AS added_digits FROM dates_core ), -- 1から順番に数値を並べます numbers (n) AS ( SELECT 1 AS n UNION ALL SELECT n + 1 AS n FROM numbers WHERE n < (SELECT max(added_digits) FROM dates) ), -- 日付各桁の和から誕生数を求めます(ただし10以上のときの加算は1回のみ) birth_numbers_core (n, birth_number) AS ( SELECT n, CASE WHEN n IN (11, 22, 33, 44) THEN n WHEN n <= 9 THEN n ELSE ( SELECT sum(cast(i as smallint)) FROM regexp_split_to_table(cast(n as varchar), '') AS i ) END birth_number FROM numbers ), -- 日付各桁の和から誕生数を求めます(完全版 - 1桁になるまで分割・加算を繰り返す) birth_numbers (n, birth_number) AS ( SELECT b1.n, b2.birth_number FROM birth_numbers_core b1 INNER JOIN birth_numbers_core b2 ON b2.n = b1.birth_number ), -- 日付に対する誕生数を取得します date_and_birth_numbers (d, birth_number) AS ( SELECT d, birth_numbers.birth_number FROM dates INNER JOIN birth_numbers ON dates.added_digits = birth_numbers.n ) -- 誕生数それぞれの登場回数を1940年〜2039年の20年ごと(&全期間)に集計します SELECT birth_number AS "誕生数", sum( CASE WHEN d BETWEEN '1940-01-01' AND '1959-12-31' THEN 1 ELSE 0 END ) AS "1940-1959", sum( CASE WHEN d BETWEEN '1960-01-01' AND '1979-12-31' THEN 1 ELSE 0 END ) AS "1960-1979", sum( CASE WHEN d BETWEEN '1980-01-01' AND '1999-12-31' THEN 1 ELSE 0 END ) AS "1980-1999", sum( CASE WHEN d BETWEEN '2000-01-01' AND '2019-12-31' THEN 1 ELSE 0 END ) AS "2000-2019", sum( CASE WHEN d BETWEEN '2020-01-01' AND '2039-12-31' THEN 1 ELSE 0 END ) AS "2020-2039", count(*) AS "1940-2039" FROM date_and_birth_numbers GROUP BY birth_number ORDER BY birth_number
こんなかんじ。
誕生数 | 1940-1959 | 1960-1979 | 1980-1999 | 2000-2019 | 2020-2039 | 1940-2039 |
---|---|---|---|---|---|---|
1 | 813 | 814 | 809 | 811 | 812 | 4059 |
2 | 135 | 46 | 4 | 468 | 546 | 1199 |
3 | 812 | 813 | 814 | 809 | 811 | 4059 |
4 | 563 | 677 | 768 | 463 | 341 | 2812 |
5 | 809 | 812 | 813 | 814 | 809 | 4057 |
6 | 396 | 298 | 246 | 814 | 806 | 2560 |
7 | 811 | 809 | 812 | 813 | 814 | 4059 |
8 | 813 | 795 | 761 | 812 | 814 | 3995 |
9 | 814 | 811 | 809 | 812 | 813 | 4059 |
11 | 677 | 768 | 807 | 341 | 266 | 2859 |
22 | 248 | 135 | 46 | 348 | 468 | 1245 |
33 | 413 | 513 | 566 | 0 | 5 | 1497 |
44 | 1 | 14 | 50 | 0 | 0 | 65 |
年代ごとの振り分け部分について解説。
sum( CASE WHEN d BETWEEN '1940-01-01' AND '1959-12-31' THEN 1 ELSE 0 END )
CASE
文を使って日付が 1940・50年代なら 1、以外は 0 に変換。
それを sum
で加算するわけです。
年間の売上を月ごとに集計して列で並べるみたいなシチュエーションでも使えるテクニックですね。
Step 6. 各年代の日数を算出
Step 5. でほぼ答えは出てしまったのですが、最終的にパーセンテージを表示したいのでもうちょい手を加えます。
割合を出すには母数が必要なので、各年代の日数を算出します。
WITH RECURSIVE dates_core (d) AS ( SELECT cast('1940-01-01' as date) AS d UNION ALL SELECT d + 1 AS d FROM dates_core WHERE d < '2039-12-31' ), -- YYYYMMDD の日付文字列を1文字ずつ分解して和を求めます dates (d, added_digits) AS ( SELECT d, ( SELECT sum(cast(i as smallint)) FROM regexp_split_to_table(to_char(d, 'YYYYMMDD'), '') AS i ) AS added_digits FROM dates_core ), -- 1から順番に数値を並べます numbers (n) AS ( SELECT 1 AS n UNION ALL SELECT n + 1 AS n FROM numbers WHERE n < (SELECT max(added_digits) FROM dates) ), -- 日付各桁の和から誕生数を求めます(ただし10以上のときの加算は1回のみ) birth_numbers_core (n, birth_number) AS ( SELECT n, CASE WHEN n IN (11, 22, 33, 44) THEN n WHEN n <= 9 THEN n ELSE ( SELECT sum(cast(i as smallint)) FROM regexp_split_to_table(cast(n as varchar), '') AS i ) END birth_number FROM numbers ), -- 日付各桁の和から誕生数を求めます(完全版 - 1桁になるまで分割・加算を繰り返す) birth_numbers (n, birth_number) AS ( SELECT b1.n, b2.birth_number FROM birth_numbers_core b1 INNER JOIN birth_numbers_core b2 ON b2.n = b1.birth_number ), -- 日付に対する誕生数を取得します date_and_birth_numbers (d, birth_number) AS ( SELECT d, birth_numbers.birth_number FROM dates INNER JOIN birth_numbers ON dates.added_digits = birth_numbers.n ), -- 誕生数それぞれの登場回数を1940年〜2039年の20年ごと(&全期間)に集計します birth_number_sums AS ( SELECT birth_number AS "誕生数", sum( CASE WHEN d BETWEEN '1940-01-01' AND '1959-12-31' THEN 1 ELSE 0 END ) AS "1940-1959", sum( CASE WHEN d BETWEEN '1960-01-01' AND '1979-12-31' THEN 1 ELSE 0 END ) AS "1960-1979", sum( CASE WHEN d BETWEEN '1980-01-01' AND '1999-12-31' THEN 1 ELSE 0 END ) AS "1980-1999", sum( CASE WHEN d BETWEEN '2000-01-01' AND '2019-12-31' THEN 1 ELSE 0 END ) AS "2000-2019", sum( CASE WHEN d BETWEEN '2020-01-01' AND '2039-12-31' THEN 1 ELSE 0 END ) AS "2020-2039", count(*) AS "1940-2039" FROM date_and_birth_numbers GROUP BY birth_number ) -- 20年ごと(&全期間)の日数を集計します SELECT sum("1940-1959") AS "日数1940-1959", sum("1960-1979") AS "日数1960-1979", sum("1980-1999") AS "日数1980-1999", sum("2000-2019") AS "日数2000-2019", sum("2020-2039") AS "日数2020-2039", sum("1940-2039") AS "日数1940-2039" FROM birth_number_sums
というわけでこう。
日数1940-1959 | 日数1960-1979 | 日数1980-1999 | 日数2000-2019 | 日数2020-2039 | 日数1940-2039 |
---|---|---|---|---|---|
7305 | 7305 | 7305 | 7305 | 7305 | 36525 |
Step 7. パーセンテージ
これで最後です。長い SQL になりましたね。
WITH RECURSIVE dates_core (d) AS ( SELECT cast('1940-01-01' as date) AS d UNION ALL SELECT d + 1 AS d FROM dates_core WHERE d < '2039-12-31' ), -- YYYYMMDD の日付文字列を1文字ずつ分解して和を求めます dates (d, added_digits) AS ( SELECT d, ( SELECT sum(cast(i as smallint)) FROM regexp_split_to_table(to_char(d, 'YYYYMMDD'), '') AS i ) AS added_digits FROM dates_core ), -- 1から順番に数値を並べます numbers (n) AS ( SELECT 1 AS n UNION ALL SELECT n + 1 AS n FROM numbers WHERE n < (SELECT max(added_digits) FROM dates) ), -- 日付各桁の和から誕生数を求めます(ただし10以上のときの加算は1回のみ) birth_numbers_core (n, birth_number) AS ( SELECT n, CASE WHEN n IN (11, 22, 33, 44) THEN n WHEN n <= 9 THEN n ELSE ( SELECT sum(cast(i as smallint)) FROM regexp_split_to_table(cast(n as varchar), '') AS i ) END birth_number FROM numbers ), -- 日付各桁の和から誕生数を求めます(完全版 - 1桁になるまで分割・加算を繰り返す) birth_numbers (n, birth_number) AS ( SELECT b1.n, b2.birth_number FROM birth_numbers_core b1 INNER JOIN birth_numbers_core b2 ON b2.n = b1.birth_number ), -- 日付に対する誕生数を取得します date_and_birth_numbers (d, birth_number) AS ( SELECT d, birth_numbers.birth_number FROM dates INNER JOIN birth_numbers ON dates.added_digits = birth_numbers.n ), -- 誕生数それぞれの登場回数を1940年〜2039年の20年ごと(&全期間)に集計します birth_number_sums AS ( SELECT birth_number AS "誕生数", sum( CASE WHEN d BETWEEN '1940-01-01' AND '1959-12-31' THEN 1 ELSE 0 END ) AS "1940-1959", sum( CASE WHEN d BETWEEN '1960-01-01' AND '1979-12-31' THEN 1 ELSE 0 END ) AS "1960-1979", sum( CASE WHEN d BETWEEN '1980-01-01' AND '1999-12-31' THEN 1 ELSE 0 END ) AS "1980-1999", sum( CASE WHEN d BETWEEN '2000-01-01' AND '2019-12-31' THEN 1 ELSE 0 END ) AS "2000-2019", sum( CASE WHEN d BETWEEN '2020-01-01' AND '2039-12-31' THEN 1 ELSE 0 END ) AS "2020-2039", count(*) AS "1940-2039" FROM date_and_birth_numbers GROUP BY birth_number ), -- 20年ごと(&全期間)の日数を集計します totals as ( SELECT sum("1940-1959") AS "日数1940-1959", sum("1960-1979") AS "日数1960-1979", sum("1980-1999") AS "日数1980-1999", sum("2000-2019") AS "日数2000-2019", sum("2020-2039") AS "日数2020-2039", sum("1940-2039") AS "日数1940-2039" FROM birth_number_sums ) -- 誕生数それぞれのパーセンテージを1940年〜2039年の20年ごと(&全期間)に算出します SELECT "誕生数", round( "1940-1959" / (SELECT "日数1940-1959" FROM totals) * 100, 2 ) AS "1940-1959", round( "1960-1979" / (SELECT "日数1960-1979" FROM totals) * 100, 2 ) AS "1960-1979", round( "1980-1999" / (SELECT "日数1980-1999" FROM totals) * 100, 2 ) AS "1980-1999", round( "2000-2019" / (SELECT "日数2000-2019" FROM totals) * 100, 2 ) AS "2000-2019", round( "2020-2039" / (SELECT "日数2020-2039" FROM totals) * 100, 2 ) AS "2020-2039", round( "1940-2039" / (SELECT "日数1940-2039" FROM totals) * 100, 2 ) AS "1940-2039(全期間)" FROM birth_number_sums ORDER BY "誕生数"
出ました!
誕生数 | 1940-1959 | 1960-1979 | 1980-1999 | 2000-2019 | 2020-2039 | 1940-2039(全期間) |
---|---|---|---|---|---|---|
1 | 11.13 | 11.14 | 11.07 | 11.10 | 11.12 | 11.11 |
2 | 1.85 | 0.63 | 0.05 | 6.41 | 7.47 | 3.28 |
3 | 11.12 | 11.13 | 11.14 | 11.07 | 11.10 | 11.11 |
4 | 7.71 | 9.27 | 10.51 | 6.34 | 4.67 | 7.70 |
5 | 11.07 | 11.12 | 11.13 | 11.14 | 11.07 | 11.11 |
6 | 5.42 | 4.08 | 3.37 | 11.14 | 11.03 | 7.01 |
7 | 11.10 | 11.07 | 11.12 | 11.13 | 11.14 | 11.11 |
8 | 11.13 | 10.88 | 10.42 | 11.12 | 11.14 | 10.94 |
9 | 11.14 | 11.10 | 11.07 | 11.12 | 11.13 | 11.11 |
11 | 9.27 | 10.51 | 11.05 | 4.67 | 3.64 | 7.83 |
22 | 3.39 | 1.85 | 0.63 | 4.76 | 6.41 | 3.41 |
33 | 5.65 | 7.02 | 7.75 | 0.00 | 0.07 | 4.10 |
44 | 0.01 | 0.19 | 0.68 | 0.00 | 0.00 | 0.18 |
44 がダントツで激レアですね。
数字だけだとわかりにくいので、Excel の条件付き書式でレア度高い順に色をつけました(列単位)。
僕の誕生数 33 は、1900年代にはいるものの2000年代以降はほぼ0 。
全期間では 33 の割合は確かに少ないけど、レア度 2位というわけでもない。
マスターナンバー(ゾロ目)の中でも 11 よりは少ないが 22 よりは多い。
なんなら 2 のほうが 44 の次にレアという意外な結果。
11 と 2 で分散してしまう(11 になった時点で計算が終わる)ので、11 に割合持っていかれてるってことですね。おもしろい。
集計期間をかえれば結果も変わってくるので、みなさんも試してみてください。
というわけで、数秘術では宇宙人みたいと言われる 33 の knsk765 がお送りしました。