C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[3]: テーブルで行方向に持っている値を、列方向に展開する方法


(過去ログ 120 を表示中)

[トピック内 7 記事 (1 - 7 表示)]  << 0 >>

■72017 / inTopicNo.1)  テーブルで行方向に持っている値を、列方向に展開する方法
  
□投稿者/ やっち (1回)-(2014/05/10(Sat) 13:54:57)

分類:[データベース全般] 

やっちと申します。よろしくお願いします。
SQLの質問です。

次のテーブル「人物」と「趣味」があります。

●テーブル「人物」
  ID |    姓    |    名    |  生年月日  | 性別 |
 ------+----------+----------+------------+------+
  0001 | 山田     | 太郎     | 1995/01/01 | 0    |
  0002 | 鈴木     | 一郎     | 1995/02/01 | 0    |
  0003 | 佐々木   | 花子     | 1995/03/01 | 1    |
 

●テーブル「趣味」
  ID | 連番 | 趣味       |
 ------+------+------------+
  0001 |    1 | 読書       |
  0001 |    2 | 映画鑑賞   |
  0002 |    1 | 野球       |
  0003 |    1 | バンド     |
  0003 |    2 | ジョギング |
  0003 |    3 | 写真       |

これから、趣味を列にして次のような結果「台帳」を得たいのですが、どのようなSQLを書いたら良いか分かりません。
どなたか教えていただけませんか?
列名の「趣味n」のnは連番と一致させる必要は無いです。
たとえば、山田さんの趣味1と趣味2が入れ替わっても問題ありません。

●テーブル「台帳」
  ID |    姓    |    名    |  生年月日  | 性別 | 趣味1  | 趣味2      | 趣味3 |
 ------+----------+----------+------------+------+--------+------------+-------+
  0001 | 山田     | 太郎     | 1995/01/01 | 0    | 読書   | 映画鑑賞   |       |
  0002 | 鈴木     | 一郎     | 1995/02/01 | 0    | 野球   |            |       |
  0003 | 佐々木   | 花子     | 1995/03/01 | 1    | バンド | ジョギング | 写真  |

よろしくお願いします。

引用返信 編集キー/
■72019 / inTopicNo.2)  Re[1]: テーブルで行方向に持っている値を、列方向に展開する方法
□投稿者/ やっち (2回)-(2014/05/10(Sat) 14:11:19)
あ、テーブル「趣味」に登録される一人当たりの件数は不定とします。
一番多趣味な人に合わせて、台帳の列数が決まります。
引用返信 編集キー/
■72020 / inTopicNo.3)  Re[1]: テーブルで行方向に持っている値を、列方向に展開する方法
□投稿者/ shu (567回)-(2014/05/10(Sat) 14:12:04)
No72017 (やっち さん) に返信

PIVOTが使用できるならPIVOTを使用するとよいです。
参考サイト:
http://technet.microsoft.com/ja-jp/library/ms177410(v=sql.105).aspx
http://ogacha.wordpress.com/2013/01/19/sql-server-%E3%81%AE-pivot-%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9/


使用できない場合は
CASE WHEN THEN ELSE END
で場合分けをすると良いです。


引用返信 編集キー/
■72021 / inTopicNo.4)  Re[2]: テーブルで行方向に持っている値を、列方向に展開する方法
□投稿者/ やっち (3回)-(2014/05/10(Sat) 14:33:52)
No72020 (shu さん) に返信

SQLServerではないので、PIVOT使えないぽいです。
なので、教えていただいたCASE WHEN THEN ELSE ENDの方法を調べてみました。
趣味が何個登録されるかわからないので、SQL一本では難しいかなー、という結論に至りました。

まず、テーブル「趣味」から最大登録数を調べて、その数によってCASE WHEN THEN ELSE ENDを用意しようと思います。
ありがとうございました!

ちなみに、こういうの、「縦持ち」とか「横持ち」とかいう表現をするんですね。
知りませんでした。
検索ワードって大事ですよねー。

しばらく掲示板見てるので、他にもいい方法がありましたら教えてください。
解決済み
引用返信 編集キー/
■72025 / inTopicNo.5)  Re[1]: テーブルで行方向に持っている値を、列方向に展開する方法
□投稿者/ 魔界の仮面弁士 (3回)-(2014/05/10(Sat) 14:52:12)
No72017 (やっち さん) に返信

厄介なパターンですね。クロス集計とも違いますし。

列名や列数が変化する場合は、動的問い合わせの方が良いと思います。
あるいは、それを表示するシステム側で変形するとか…。


とりあえず、Oracle の場合はこのような対処方法があります。
列が分かれてるわけでは無いですけど。

--- サンプルデータの準備 --
CREATE TABLE 人物
(ID CHAR(4)  
, 姓  VARCHAR2(10) 
, 名  VARCHAR2(10) 
, 生年月日 DATE
, 性別 NUMBER(1)
, CONSTRAINT 人物PK PRIMARY KEY( ID )
);

CREATE TABLE 趣味
( ID CHAR(4)
, 連番 NUMBER(3) 
, 趣味 VARCHAR2(10) 
, CONSTRAINT 趣味PK PRIMARY KEY( ID, 連番 )
);

INSERT ALL
  INTO 人物 VALUES ( '0001', '山田'  , '太郎', DATE '1995-01-01', 0 )
  INTO 人物 VALUES ( '0002', '鈴木'  , '一郎', DATE '1995-02-01', 0 )
  INTO 人物 VALUES ( '0003', '佐々木', '花子', DATE '1995-03-01', 1 )
  INTO 趣味 VALUES ( '0001',  1, '読書' )
  INTO 趣味 VALUES ( '0001',  2, '映画鑑賞' )
  INTO 趣味 VALUES ( '0002',  1, '野球' )
  INTO 趣味 VALUES ( '0003',  1, 'バンド' )
  INTO 趣味 VALUES ( '0003',  2, 'ジョギング' )
  INTO 趣味 VALUES ( '0003',  3, '写真' )
SELECT * FROM DUAL;

--- 問い合わせ --

SELECT
  人物.ID
, 人物.姓
, 人物.名
, 人物.生年月日
, 人物.性別
, wmsys.wm_concat(趣味.趣味) 趣味リスト
FROM
  人物 LEFT JOIN 趣味 ON
  人物.ID = 趣味.ID
GROUP BY
  人物.ID
, 人物.姓
, 人物.名
, 人物.生年月日
, 人物.性別

引用返信 編集キー/
■72044 / inTopicNo.6)  Re[2]: テーブルで行方向に持っている値を、列方向に展開する方法
□投稿者/ やっち (4回)-(2014/05/12(Mon) 13:49:54)
No72025 (魔界の仮面弁士 さん) に返信

書き込みありがとうございます!
動的にSQL作る方針で行こうと思います。

Oracleのwmsys.wm_concat関数の情報ありがとうございました。
初めて知りました。
いつかのために調べてみます。
解決済み
引用返信 編集キー/
■72050 / inTopicNo.7)  Re[3]: テーブルで行方向に持っている値を、列方向に展開する方法
□投稿者/ 魔界の仮面弁士 (4回)-(2014/05/13(Tue) 12:10:18)
No72044 (やっち さん) に返信
> 動的にSQL作る方針で行こうと思います。
もしくは、趣味の上限数を想定して多めに列を組み上げるとかですかね。


> Oracleのwmsys.wm_concat関数の情報ありがとうございました。

No72021 で「SQLServerではないので、PIVOT使えない」と仰っていましたが、
そもそも、使用しておられるデータベースは何だったのでしょうか?
それによって、使える SQL 構文も全然異なってきます。


たとえば Access であれば、動的 SQL にせずとも処理できます。
使うのは、TRANSFROM 〜 PIVOT 構文です。

※Access / JET
-------------------------------------------------------------------
TRANSFORM FIRST(趣味)
SELECT ID, 姓, 名, 生年月日, 性別 FROM [
  SELECT 人物.ID, 人物.姓, 人物.名, 人物.生年月日, 人物.性別, 趣味.連番, 趣味.趣味
  FROM 人物 INNER JOIN 趣味 ON 人物.ID = 趣味.ID
]. AS WK GROUP BY ID, 姓, 名, 生年月日, 性別
ORDER BY ID
PIVOT 連番
-------------------------------------------------------------------

この場合、『趣味.連番』がそのまま列名として使われますので、
「1」「2」「3」といった列名になります。連番に抜けがあっても構いません。

INNER JOIN を LEFT JOIN にした場合は、無趣味の人もカウントされますが、
この場合、「<>」という列も追加されます。


一方、Oracle の PIVOT の場合は、動的 SQL にしないと難しいと思います。

※Oracle (11g以降)
-------------------------------------------------------------------
SELECT
  人物.ID
, 人物.姓
, 人物.名
, 人物.生年月日
, 人物.性別
, WK.*
FROM
  人物 LEFT JOIN 趣味 PIVOT (MAX(趣味) FOR 連番 IN (1, 2, 3, 4, 5)) WK
  ON 人物.ID = WK.ID
ORDER BY
  人物.ID
-------------------------------------------------------------------
IN で指定した連番で列が作られるため、たとえば趣味番号 4 を
誰も使っていなかったとしても、列「4」は生成されることになります。



※Oracle 8i Enterprise もしくは Oracle 9i Standard 以降
-------------------------------------------------------------------
SELECT WK.* FROM (
  SELECT
    人物.ID
  , 人物.姓
  , 人物.名
  , 人物.生年月日
  , 人物.性別
  , 趣味.連番
--, COUNT(趣味.連番) OVER (PARTITION BY 趣味.ID) AS 趣味数
  , LEAD( 趣味.趣味, 0 ) OVER (PARTITION BY 趣味.ID ORDER BY 趣味.連番) AS 趣味1
  , LEAD( 趣味.趣味, 1 ) OVER (PARTITION BY 趣味.ID ORDER BY 趣味.連番) AS 趣味2
  , LEAD( 趣味.趣味, 2 ) OVER (PARTITION BY 趣味.ID ORDER BY 趣味.連番) AS 趣味3
  , LEAD( 趣味.趣味, 3 ) OVER (PARTITION BY 趣味.ID ORDER BY 趣味.連番) AS 趣味4
  , LEAD( 趣味.趣味, 4 ) OVER (PARTITION BY 趣味.ID ORDER BY 趣味.連番) AS 趣味5
  FROM
    人物 LEFT JOIN 趣味 ON 人物.ID = 趣味.ID
) WK WHERE NVL(WK.連番, 1) = 1
ORDER BY WK.ID
-------------------------------------------------------------------
この手法では、抜けのあった趣味番号は前倒しに配置されます。
たとえば、田中さんの趣味番号が「1, 2, 5」となっていて 3, 4 が無い場合、
「趣味3」列に 連番5 の趣味が配置されます。


※Oracle
-------------------------------------------------------------------
SELECT
  人物.ID
, 人物.姓
, 人物.名
, 人物.生年月日
, 人物.性別
, MAX(DECODE(趣味.連番, 1, 趣味.趣味)) AS 趣味1
, MAX(DECODE(趣味.連番, 2, 趣味.趣味)) AS 趣味2
, MAX(DECODE(趣味.連番, 3, 趣味.趣味)) AS 趣味3
, MAX(DECODE(趣味.連番, 4, 趣味.趣味)) AS 趣味4
, MAX(DECODE(趣味.連番, 5, 趣味.趣味)) AS 趣味5
FROM
  人物 LEFT JOIN 趣味 ON
  人物.ID = 趣味.ID
GROUP BY
  人物.ID
, 人物.姓
, 人物.名
, 人物.生年月日
, 人物.性別
ORDER BY
  人物.ID
-------------------------------------------------------------------
こちらは PIVOT 版と同様、抜けのあった趣味番号の列も作られます。
田中さんの趣味番号が「1, 2, 5」となっていて 3, 4 が無い場合、
「趣味3」「趣味4」列が空になり、「趣味5」に趣味の名前が記載されます。

引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -