質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Q&A

解決済

4回答

1787閲覧

SQLを用いた複数のグループで分けるクロス集計をcase文で実行する記述について

Carlos_is_only

総合スコア44

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

0グッド

0クリップ

投稿2021/08/24 07:03

参考書に記載されていたcase文の読み方はよくわかりません

次のようなテーブルを作成しデータを入れました。

sql

1DROP TABLE IF EXISTS master_customer; 2create table master_customer( 3user_id char(008), 4age int, 5sex char(008), 6user_place char(016) 7); 8insert into master_customer values 9('UID0001',45,'M','Kanagawa'), 10('UID0002',31,'M','Osaka'), 11('UID0003',22,'M','Tokyo'), 12('UID0004',25,'W','Tokyo'), 13('UID0005',36,'W','Tokyo'), 14('UID0006',40,'W','Kanagawa'), 15('UID0007',34,'M','Kanagawa'), 16('UID0008',24,'W','Tokyo'), 17('UID0009',30,'M','Tokyo'), 18('UID0010',25,'W','Osaka'), 19('UID0011',20,'W','Tokyo'), 20('UID0012',31,'W','Osaka')

そこで次のようなSQLを実行し
それぞれの地域の男女の数をsqlで集計しました。

sql

1WITH count_tmp AS(              -- (1) 2 SELECT 3 sex, 4 user_place, 5 COUNT(user_id) AS user_count 6 FROM 7 master_customer 8 GROUP BY 9 user_place, 10 sex 11 ORDER BY 12 user_place DESC 13), 14tmp AS( -- (2) 15 SELECT 16 *, 17 ROW_NUMBER() OVER( 18 PARTITION BY sex 19 ORDER BY user_place 20 ) AS sequence 21 FROM 22 count_tmp 23) 24SELECT -- (3) 25 sex, 26 MAX(CASE sequence WHEN 1 THEN user_count ELSE NULL END) AS Kanagawa, 27 MAX(CASE sequence WHEN 2 THEN user_count ELSE NULL END) AS Osaka, 28 MAX(CASE sequence WHEN 3 THEN user_count ELSE NULL END) AS Tokyo 29FROM 30 tmp 31GROUP BY 32 sex 33;

集計用SQLについて
(1)と(2)のSQLは理解できました。
(1)はこれはuser_placeと性別でグループで分割した後にグループごとのデータ数を取得しています。さらに(2)ではウインドウ関数を使用して性別ごとに分割し場所ごとにorder byで並び替えること場所とsequenceという列の値がそれぞれ対応するようなデータが作成されます。
ここまでは理解できたのですが
(3)のcase文が理解できません
そもそもこのcase文は(1)、(2)によって作成されたデータがさらに性別でグループ分けされたデータを1文づつ相関サブクエリのように評価しているような記載が参考書にありました。
case文は集約関数であれば問題はないようです
case文はsequenseの値を一文づつ評価して該当しないものに関してはNULLを返し
sequenseというテーブルの中にNULLとそれ以外のデータが作りださせそれが集約関数でNULLが排除され値を返すイメージですか?
case文によって疑似的なテーブルが作成されるのでしょうか?
なんだかこのSQLの実行タイミングなどなかなかイメージが付きづらいので
どなたかどのようにcase文が結果を返しているの解説していただけますでしょうか?

以上です、よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

こんな書換もあります。

SQL

1select sex 2 , count(user_place = 'Kanagawa' or null) Kanagawa 3 , count(user_place = 'Osaka' or null) Osaka 4 , count(user_place = 'Tokyo' or null) Tokyo 5from master_customer 6group by sex

共通テーブル式のテーブルを使うならこんな書換とか。

SQL

1select 2 sex 3 , (array_agg(user_count)) [1] Kanagawa 4 , (array_agg(user_count)) [2] Osaka 5 , (array_agg(user_count)) [3] Tokyo 6from 7 (select sex, user_count from tmp order by sex, sequence) tmp 8group by 9 sex

しかし一次元配列(集合)になるのか
僕は知らなかった。

投稿2021/08/24 09:46

編集2021/08/26 06:49
k.fujisawa

総合スコア39

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mayu-

2021/08/24 11:01

私の回答、一次元配列(集合) という表現は確かに変でした。ご指摘感謝いたします。
k.fujisawa

2021/08/24 16:58

そうなんですね。指摘だなんてそんな自覚は全くなかったです!
mayu-

2021/08/24 22:18

肯定的に受け取っていますので お気になさらないで下さい 今回も私は言葉の選択がまずいですね。 PostgreSQLは数あるDBの中でも 配列を扱う関数や機能が特に充実しているため 「配列」という言葉で 閲覧された方に誤解を与えないようにする 工夫と配慮が足りていませんでした。
k.fujisawa

2021/08/25 00:25

丁寧な回答ありがとうございます。言葉の選択はなかなか難しいですよね。
guest

0

ご提示いただいたSQL
_ WITH count_tmp AS( ... GROUP BY sex ;

以下にご紹介するシンプルなSQLに書き換えが可能です。

SQL

1SELECT sex 2 , Sum( case user_place when 'Kanagawa' then 1 else 0 end ) Kanagawa 3 , Sum( case user_place when 'Osaka' then 1 else 0 end ) Osaka 4 , Sum( case user_place when 'Tokyo' then 1 else 0 end ) Tokyo 5FROM master_customer 6GROUP BY sex ;

次に、上記のSQLについて動作を説明いたしますと

case文によって疑似的なテーブルが作成されるのでしょうか?

「 疑似的なテーブル 」といいますか、case文によって作成されるのは
列ごとの一次元配列(集合)になります。
case文を実行した段階では、step1 のようなイメージになると申し上げれば
ご理解いただけるでしょうか。

  • step1
sexkanagawaosakatokyo
M[1,0,0,1,0][0,1,0,0,0][0,0,1,0,1]
W[1,0,0,0,0,0,0][0,0,0,1,1,0,0][0,1,1,0,0,1,1]

  
次に step1 の配列の中に含まれる数値を、sum関数を用いて合算します

  • result
sexkanagawaosakatokyo
M212
W124

 
参考書の記載どおりになりました。

追記

k.fujisawaさんのご指摘を受けて回答を見返すと
先の私の回答、一次元配列(集合)という表現は確かに変でした。

インラインビューを用い、多段クエリにしたら

case文によって疑似的なテーブルが作成されるのでしょうか?

の「 疑似的なテーブルが作成される 」に近いSQLになりそうですね。

SQL

1SELECT sex 2 , sum(k) Kanagawa 3 , sum(o) Osaka 4 , sum(t) Tokyo 5FROM 6( 7 SELECT sex 8 , case user_place when 'Kanagawa' then 1 else 0 end k 9 , case user_place when 'Osaka' then 1 else 0 end o 10 , case user_place when 'Tokyo' then 1 else 0 end t 11 FROM master_customer 12) q 13GROUP BY sex ;

このSQLだとどうでしょうか。
インラインビューqは「 疑似的なテーブル 」になっており、
結果セットは以下のようになります。

sexkot
M100
M010
M001
M100
M001
W100
W001
W001
W010
W010
W001
W001

投稿2021/08/24 08:35

編集2021/08/24 10:59
mayu-

総合スコア335

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Carlos_is_only

2021/08/27 00:58

なるほど疑似的なテーブルがサブクエリ(?)として実行された結果作成され 集約関数で結果を取得できるということが理解できました。
guest

0

先のコメントの続きです

A
B
A
B

上記のテーブルから値が"A"のレコードの数を合計するロジックを考えた時

SQL

1SELECT SUM(CASE WHEN="A" THEN 1 ELSE 0 END) AS 合計 2FROM TBL

のロジックは

For i = 1 To 4 合計 = 合計 + IIf(値(i) = "A", 1, 0) Next

というイメージ

SQL

1SELECT SUM(A) AS 合計 2FROM (SELECT CASE WHEN="A" THEN 1 ELSE 0 END AS A 3 FROM TBL)

のロジックは

For i = 1 To 4 A(i) = IIf(値(i) = "A", 1, 0) Next For i = 1 To 4 合計 = 合計 + A(i) Next

というイメージ

結果的にはどちらも同じになります

投稿2021/08/27 12:35

takanaweb5

総合スコア359

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

SQLを理解するには、該当箇所を単純化して実行し結果を確認する方法が有効です。

(3)部分についてgroup byする前のSQLを実行してみます

sql

1SELECT 2 sex, 3 sequence, 4 user_count, 5 CASE sequence WHEN 1 THEN user_count ELSE NULL END AS Kanagawa, 6 CASE sequence WHEN 2 THEN user_count ELSE NULL END AS Osaka, 7 CASE sequence WHEN 3 THEN user_count ELSE NULL END AS Tokyo 8FROM 9 tmp

結果

sexsequenceuser_countkanagawaosakatokyo
M122(null)(null)
M21(null)1(null)
M32(null)(null)2
W111(null)(null)
W22(null)2(null)
W34(null)(null)4

(3)部分は上記の結果に対して集計関数を実行したのと同じです

SQL

1SELECT 2 sex, 3 MAX(Kanagawa), 4 MAX(Osaka), 5 MAX(Tokyo) 6FROM (SELECT 7 sex, 8 CASE sequence WHEN 1 THEN user_count ELSE NULL END AS Kanagawa, 9 CASE sequence WHEN 2 THEN user_count ELSE NULL END AS Osaka, 10 CASE sequence WHEN 3 THEN user_count ELSE NULL END AS Tokyo 11 FROM 12 tmp 13 ) t 14GROUP BY 15 sex

結果

sexkanagawaosakatokyo
M212
W124

投稿2021/08/24 11:39

takanaweb5

総合スコア359

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Carlos_is_only

2021/08/27 00:56

なるほど、今回の場合ですとtmpテーブルからデータをcase文がサブクエリとして実行されさらにそこで集約関数が実行されnullデータが排除されるということですね
takanaweb5

2021/08/27 12:16

>tmpテーブルからデータをcase文がサブクエリとして実行されさらにそこで集約関数が実行され 結果的にサブクエリとして実行された結果の集計結果と一致しますが、SQLの実行計画でサブクエリが実行されているわけではありません。 ただ、結果的にサブクエリとして実行された場合と結果は一致しますので、SUM関数などの集計関数の中で、CASE文が記述されている場合は、サブクエリをイメージして考えると理解しやすいでしょう。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問