以下のテーブルを基に体重ヒストグラムを作成したいです
tbl_student
組 名前 体重
1 a 38
1 b 52
1 c 56
1 d 62
2 e 45
2 f 65
2 g 78
このテーブルを集計し、以下の形にしたいです
tbl_weight_histogram
組 体重レンジ 人数
1 30 1
1 40 0
1 50 2
1 60 1
2 30 0
2 40 1
2 50 0
2 60 2
・体重レンジは30台、40台、50台、60台以上とする
・人数0人の場合も0件としてカウントする
特に2つ目の条件で困っております
外部テーブルを用意するのは構いません
どなたかご助言よろしくお願いします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
図でイメージするOracleのSQL全集 第1回 さまざまな結合
https://blogs.oracle.com/otnjp/oracle-sql-image-001
の「Partitioned Outer Join」を見ながら
SQLFiddleのOracle11gR2環境で作ってみました :-)
sql
1with RangeTable(体重レンジ,下限,上限) as( 2select 30, 0, 39 from dual union 3select 40,40, 49 from dual union 4select 50,50, 59 from dual union 5select 60,60,999 from dual), 6tbl_student(組,名前,体重) as( 7select 1,'a',38 from dual union 8select 1,'b',52 from dual union 9select 1,'c',56 from dual union 10select 1,'d',62 from dual union 11select 2,'e',45 from dual union 12select 2,'f',65 from dual union 13select 2,'g',78 from dual) 14select b.組,a.体重レンジ,count(b.体重) as 人数 15 from RangeTable a 16 Left Join tbl_student b 17partition by(b.組) 18 on b.体重 between a.下限 and a.上限 19group by b.組,a.体重レンジ 20order by b.組,a.体重レンジ; 21 22| 組 | 体重レンジ | 人数 | 23|----|------------|------| 24| 1 | 30 | 1 | 25| 1 | 40 | 0 | 26| 1 | 50 | 2 | 27| 1 | 60 | 1 | 28| 2 | 30 | 0 | 29| 2 | 40 | 1 | 30| 2 | 50 | 0 | 31| 2 | 60 | 2 |
投稿2017/11/17 21:00
編集2020/07/26 08:37総合スコア1147
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
図でイメージするOracleのSQL全集 第8回 PivotとUnPivot
https://blogs.oracle.com/otnjp/oracle-sql-image-008
を見ながら、SQLFiddleのOracle11gR2環境で作ってみました :-)
sql
1with tbl_student(組,名前,体重) as( 2select 1,'a',38 from dual union 3select 1,'b',52 from dual union 4select 1,'c',56 from dual union 5select 1,'d',62 from dual union 6select 2,'e',45 from dual union 7select 2,'f',65 from dual union 8select 2,'g',78 from dual), 9tmp as( 10select 組, 11count(case when 体重 between 0 and 39 then 1 end) as cnt1, 12count(case when 体重 between 40 and 49 then 1 end) as cnt2, 13count(case when 体重 between 50 and 59 then 1 end) as cnt3, 14count(case when 体重 between 60 and 999 then 1 end) as cnt4 15 from tbl_student 16group by 組) 17select * 18 from tmp 19UnPivot(人数 for 体重レンジ 20 in(cnt1 as 30, 21 cnt2 as 40, 22 cnt3 as 50, 23 cnt4 as 60)) 24order by 組,体重レンジ; 25 26| 組 | 体重レンジ | 人数 | 27|----|------------|------| 28| 1 | 30 | 1 | 29| 1 | 40 | 0 | 30| 1 | 50 | 2 | 31| 1 | 60 | 1 | 32| 2 | 30 | 0 | 33| 2 | 40 | 1 | 34| 2 | 50 | 0 | 35| 2 | 60 | 2 |
投稿2017/11/17 21:02
編集2020/07/26 08:41総合スコア1147
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ベストアンサー
この手の処理はレンジをテーブルに持ったほうがよいです
とくに「2 g 78 」のように70超を60に押し込めようとしているのでしたらなおさらです
一応学生にとって59.5kgと60.0kgは大きな違いなのでweightはdoubleにしておきました
- 元データ
SQL
1create table weight(weight_range int,weight_min double,weight_max double); 2insert into weight values 3(30,0,39.99), /*30を下回ってもレンジは30*/ 4(40,40,49.99), 5(50,50,59.99), 6(60,60,999.99); /*70を超えてもレンジは60*/ 7 8create table student(class int,name varchar(20),weight double); 9insert into student values 10(1,'a',38), 11(1,'b',52), 12(1,'c',56), 13(1,'d',62), 14(2,'e',45), 15(2,'f',65), 16(2,'g',78); 17
ホントはクラスもテーブルを使うほうがいいのですが、今回はdistinctで処理します
SQL
1select t1.class,t1.weight_range,coalesce(t4.count,0) as count 2from (select class,weight_range from weight 3inner join (select distinct class from student) as sub on 1) as t1 4left join ( 5select class,weight_range,count(*) as count 6from weight as t2 7inner join student as t3 on t3.weight between t2.weight_min and t2.weight_max 8group by class,weight_range 9) as t4 on t1.class=t4.class and t1.weight_range=t4.weight_range 10order by class,weight_range 11
投稿2017/11/15 03:13
総合スコア114839
0
以下は共通表式 (CTE) を用いたPostgreSQLでの例。一時的な表をSQL中に記述することが出来ます。
体重レンジのCTEとクラス一覧のCTEをクロス結合して集計パターンを網羅した中間テーブルを作り、そこにtbl_studentを外部結合して集計しています。
共通表式はMySQLでは8.0からでしか利用できませんが、他の主要なDBMSでは以前からサポートされています。
sql
1WITH cte_w ( 2 weight_range 3) AS ( 4 SELECT 30 UNION ALL 5 SELECT 40 UNION ALL 6 SELECT 50 UNION ALL 7 SELECT 60 8), 9cte_c ( 10 class 11) AS ( 12 SELECT 1 UNION ALL 13 SELECT 2 14) 15SELECT 16 tmp.class 17 ,tmp.weight_range 18 ,COUNT(tbl_student.weight) 19FROM 20 (SELECT * FROM cte_c, cte_w) AS tmp 21 LEFT OUTER JOIN tbl_student 22 ON tmp.class = tbl_student.class 23 AND ( 24 tmp.weight_range = TRUNC(tbl_student.weight, -1) 25 OR ( 26 tmp.weight_range = 60 27 AND tbl_student.weight >= 60 28 ) 29 ) 30GROUP BY 31 tmp.class 32 ,tmp.weight_range 33ORDER BY 34 tmp.class 35 ,tmp.weight_range 36;
投稿2017/11/14 21:32
編集2017/11/14 22:36総合スコア1149
0
まず、体重を10単位に丸めるには TRUNCATE関数 を使います。(テーブルを削除する TRUNCATE文 とは違うので注意)
MySQL :: MySQL 5.6 リファレンスマニュアル :: 12.6.2 数学関数
sql
1SELECT TRUNCATE(38, -1); 2-> 30
つぎに、レコードが無い体重も0人と取得したい件は
SQLで集約した時系列データの値が0となる行を補完する - Qiita
などSQLで無理矢理実装する方法もありますが、存在しないデータを扱うのはDBのお仕事では無いので、できればアプリ側で作るのがよいと思います。
投稿2017/11/14 14:45
総合スコア3095
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。