MySQLでピボットテーブルの出力を返すにはどうすればよいですか?


312

次のようなMySQLテーブルがある場合:

company_nameアクションのページ数
-------------------------------
A社の印刷3
A社プリント2
A社の印刷3
B社のメール   
B社プリント2
B社プリント2
B社プリント1
A社の印刷3

MySQLクエリを実行して次のような出力を取得することは可能ですか?

company_name EMAIL PRINT 1ページPRINT 2ページPRINT 3ページ
-------------------------------------------------- -----------
CompanyA 0 0 1 3
CompanyB 1 1 2 0

考え方はpagecount異なる可能性があるので、出力列の量はそれを反映する必要があります。各action/ pagecountペアに1列、次にあたりのヒット数company_name。これがピボットテーブルと呼ばれるかどうかはわかりませんが、誰かがそれを示唆していますか?


3
これはピボットと呼ばれ、SQLの外でこの変換を行う方がはるかに高速です。
NB

1
Excelはこのようなことをすり抜けて、 "CROSSTAB"演算子がないため、MySQLでは本当に難しい:(
Dave Rix

はい、現在Excelで手動で行われており、自動化を試みています。
peku

3
ここで私はステップバイステップの例を見つけました:ピボットテーブルを自動化する方法。and this
Devid G

1
@giannischristofakis-それは本当にあなたとあなたの同僚がより簡単であると考えるものに依存します。コメントを投稿してから(4年)、テクノロジーはかなり追いついたので、アプリケーションでもSQLでも、あなたがより良いと感じるものに完全に依存しています。たとえば、私の仕事では同様の問題を扱っていますが、SQLとアプリ内アプローチの両方を組み合わせています。基本的に、私は意見を述べた答えを出すこと以外はあなたを助けることができず、それはあなたが必要とするものではありません:)
NB

回答:


236

これは基本的にあるピボットテーブル。

これを達成する方法についての素晴らしいチュートリアルはここにあります:http : //www.artfulsoftware.com/infotree/qrytip.php?id=78

この投稿を読んで、このソリューションをニーズに合わせて調整することをお勧めします。

更新

上記のリンクが現在利用できなくなった後、私はここでmysqlピボットの回答を検索しているすべての人にいくつかの追加情報を提供する義務があると感じています。それは本当に膨大な量の情報を持っていたので、ここからすべてをここに置くことはしません(さらに、彼らの膨大な知識をコピーしたくないだけなので)さらに、ピボットに対処する方法についていくつかアドバイスをします最初に質問を行ったpekuの例を使用して、一般的にSQLの方法を示します。

リンクがすぐに戻ってくるかもしれませんが、私はそれを監視します。

スプレッドシートの方法...

多くの人は、MSExcel、OpenOffice、またはその他のスプレッドシートツールなどのツールをこの目的で使用しています。これは有効なソリューションです。そこにデータをコピーし、GUIが提供するツールを使用してこれを解決してください。

しかし...これは問題ではありませんでした。データをスプレッドシートに取り込む方法、問題のあるスケーリングなど、いくつかの欠点につながる可能性もあります。

SQLの方法...

彼のテーブルは次のようになります。

CREATE TABLE `test_pivot` (
  `pid` bigint(20) NOT NULL AUTO_INCREMENT,
  `company_name` varchar(32) DEFAULT NULL,
  `action` varchar(16) DEFAULT NULL,
  `pagecount` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`pid`)
) ENGINE=MyISAM;

次に、彼/彼女の希望するテーブルを調べます。

company_name    EMAIL   PRINT 1 pages   PRINT 2 pages   PRINT 3 pages
-------------------------------------------------------------
CompanyA        0       0               1               3
CompanyB        1       1               2               0

行(EMAILPRINT x pages)は条件に似ています。主なグループ分けはcompany_nameです。

条件を設定するために、これはCASE-statement を使用することをかなり叫びます。何かによってグループ化するためには、よく、使用... GROUP BY

このピボットを提供する基本的なSQLは次のようになります。

SELECT  P.`company_name`,
    COUNT(
        CASE 
            WHEN P.`action`='EMAIL' 
            THEN 1 
            ELSE NULL 
        END
    ) AS 'EMAIL',
    COUNT(
        CASE 
            WHEN P.`action`='PRINT' AND P.`pagecount` = '1' 
            THEN P.`pagecount` 
            ELSE NULL 
        END
    ) AS 'PRINT 1 pages',
    COUNT(
        CASE 
            WHEN P.`action`='PRINT' AND P.`pagecount` = '2' 
            THEN P.`pagecount` 
            ELSE NULL 
        END
    ) AS 'PRINT 2 pages',
    COUNT(
        CASE 
            WHEN P.`action`='PRINT' AND P.`pagecount` = '3' 
            THEN P.`pagecount` 
            ELSE NULL 
        END
    ) AS 'PRINT 3 pages'
FROM    test_pivot P
GROUP BY P.`company_name`;

これにより、目的の結果が非常に速く提供されます。このアプローチの主な欠点は、ピボットテーブルに必要な行が増えるほど、SQLステートメントで定義する必要のある条件が増えます。

これも対処できるため、人々は準備されたステートメント、ルーチン、カウンターなどを使用する傾向があります。

このトピックに関するいくつかの追加リンク:


4
リンクは今のところ機能しているようです...再びダウンする場合は、これらを試してください:Googleのキャッシュwebcache.googleusercontent.com/…またはInternet Wayback Machine(web.archive.org/web/20070303120558 * / artfulsoftware.com/ infotree / queries.php
Lykegenes

リンクには、次のURLからアクセスできます。artfulsoftware.com
infotree/

1
「if」、「case」、または「GROUP_CONCAT」を使用せずにピボットテーブルを生成する別の方法があります:en.wikibooks.org/wiki/MySQL/Pivot_table
user2513149

ハットがデフォルトの動作であるため、ELSE NULLをCASEから削除できます(条件付き集計で十分です)
Caius Jard

86

私のソリューションはピボットなしのT-SQLです。

SELECT
    CompanyName,  
    SUM(CASE WHEN (action='EMAIL') THEN 1 ELSE 0 END) AS Email,
    SUM(CASE WHEN (action='PRINT' AND pagecount=1) THEN 1 ELSE 0 END) AS Print1Pages,
    SUM(CASE WHEN (action='PRINT' AND pagecount=2) THEN 1 ELSE 0 END) AS Print2Pages,
    SUM(CASE WHEN (action='PRINT' AND pagecount=3) THEN 1 ELSE 0 END) AS Print3Pages
FROM 
    Company
GROUP BY 
    CompanyName

2
これはPostgreSQLでも機能します。私はこれがあるとしてPostgresの上のクロス集計拡張子を使用するよりも、この方法を好むクリーナー
itsols

2
「私のソリューションはピボットのないT-SQLにあります。」 SQL Serverだけでなく、ANSI SQL標準に準拠するほとんどのデータベースベンダーで動作するはずです。SUM()文字列をピボットする必要がある場合にのみ数値データを処理できることに注意してくださいMAX()
Raymond Nijland

1
私が考えるケースがでunnesesaryあるSUM(CASE WHEN (action='PRINT' AND pagecount=1) THEN 1 ELSE 0 END)あなただけ行うことができ、SUM(action='PRINT' AND pagecount=1)条件はに変換されますので、1とき真と0する場合はfalse
kajacx

1
@kajacxはい、そのようなブール操作を持たないデータベースでは必要です。「すべてのdBで機能するより長い構文」と「...でのみ機能するより短い構文」のどちらかを選択した場合、前者を選択します
Caius Jard

66

MySQLの場合、あなたは直接に条件を置くことができるSUM()機能、それがされるブールとして評価0または1ので、あなたはあなたのカウントは使用しなくても、あなたの基準に基づいてすることができIF/CASE文を

SELECT
    company_name,  
    SUM(action = 'EMAIL')AS Email,
    SUM(action = 'PRINT' AND pagecount = 1)AS Print1Pages,
    SUM(action = 'PRINT' AND pagecount = 2)AS Print2Pages,
    SUM(action = 'PRINT' AND pagecount = 3)AS Print3Pages
FROM t
GROUP BY company_name

DEMO


1
それは本当にすてきなものです。これが他のプラットフォーム(Postgresなど)で標準に準拠しているかどうかを知っていますか?
itols 2014年

3
@itsolsいいえ、Mysql固有のみ
M Khalid


2
SQLiteでも機能
SBF 2017年

37

動的ピボットの場合はGROUP_CONCAT、と一緒に使用しCONCATます。GROUP_CONCATの機能は、様々なオプションを持つ1つの文字列にグループからの文字列を連結します。

SET @sql = NULL;
SELECT
    GROUP_CONCAT(DISTINCT
    CONCAT(
      'SUM(CASE WHEN action = "',
      action,'"  AND ', 
           (CASE WHEN pagecount IS NOT NULL 
           THEN CONCAT("pagecount = ",pagecount) 
           ELSE pagecount IS NULL END),
      ' THEN 1 ELSE 0 end) AS ',
      action, IFNULL(pagecount,'')

    )
  )
INTO @sql
FROM
  t;

SET @sql = CONCAT('SELECT company_name, ', @sql, ' 
                  FROM t 
                   GROUP BY company_name');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

ここでデモ


2
パチェリエ、本当の男ですが、ダイナミックなピボットのための最高のアプローチの1つ
Abhishek Gupta

2
これは、「アクション」列に多くの値がある場合、またはそのリストが時間の経過とともに大きくなることが予想される場合に適切に機能します。各値のケースステートメントの記述には時間がかかり、最新の状態を維持することが難しいためです。
Patrick Murphy、

23

ブール論理を使用したstardard-SQLバージョン:

SELECT company_name
     , COUNT(action = 'EMAIL' OR NULL) AS "Email"
     , COUNT(action = 'PRINT' AND pagecount = 1 OR NULL) AS "Print 1 pages"
     , COUNT(action = 'PRINT' AND pagecount = 2 OR NULL) AS "Print 2 pages"
     , COUNT(action = 'PRINT' AND pagecount = 3 OR NULL) AS "Print 3 pages"
FROM   tbl
GROUP  BY company_name;

SQL Fiddle。

どうやって?

TRUE OR NULL 利回りTRUE
FALSE OR NULL利回りNULL
NULL OR NULL利回りNULL
そしてCOUNT、null以外の値のみをカウントします。ボイラ。


@アーウィン、しかし3つの列があることをどうやって知っていますか?5があるとどうなりますか?10?20?
Pacerier、2015

@Pacerier:問題の例はそれを示唆しているようです。どちらの方法でも、SQL は戻り値の型を知る必要があります。完全に動的なクエリを行うことはできません。出力列の数が変化する可能性がある場合は、2つのステップが必要です。1つ目はクエリの作成、2つ目はクエリの実行です。
Erwin Brandstetter 2015

11

正解は次のとおりです。

select table_record_id,
group_concat(if(value_name='note', value_text, NULL)) as note
,group_concat(if(value_name='hire_date', value_text, NULL)) as hire_date
,group_concat(if(value_name='termination_date', value_text, NULL)) as termination_date
,group_concat(if(value_name='department', value_text, NULL)) as department
,group_concat(if(value_name='reporting_to', value_text, NULL)) as reporting_to
,group_concat(if(value_name='shift_start_time', value_text, NULL)) as shift_start_time
,group_concat(if(value_name='shift_end_time', value_text, NULL)) as shift_end_time
from other_value
where table_name = 'employee'
and is_active = 'y'
and is_deleted = 'n'
GROUP BY table_record_id

1
これはあなたが手元に持っていた単なる例ですか?other_valueテーブルの構造は何ですか?
Patrick Murphy、

1
「正しい答えは:」ほとんどの場合ではない、欠落しているとしてSET、単純に...起こることが予想外の結果を意味し、エラーなしで文字列を切り捨て1024 GROUP_CONCAT後GROUP_CONCAT 1024に制限されているdefualt値を高めるために、クエリを
レイモンドNijland

申し訳ありませんが、詳細を覚えることはできません。私は楽しみのために何かをしてから、プロジェクト全体を忘れるか破壊します。しかし、私が挑戦に出会ったとき、私はそれをどのように修正したかを共有します。私の例は非常に詳細ではないことを知っていますが、それが彼らが何に対抗しているのかを知っている人々に指示を与えるかもしれないと思います:)
Talha

9

MySQLピボットテーブルジェネレーターと呼ばれるツールがあります。これは、後でExcelにエクスポートできるWebベースのピボットテーブルを作成するのに役立ちます(必要な場合)。データが単一のテーブルまたは複数のテーブルにある場合に機能します。

列のデータソース(動的列をサポートします)、行、テーブルの本体の値、およびテーブルの関係(存在する場合)を指定するだけです。 MySQLピボットテーブル

このツールのホームページはhttp://mysqlpivottable.netです


3
select t3.name, sum(t3.prod_A) as Prod_A, sum(t3.prod_B) as Prod_B, sum(t3.prod_C) as    Prod_C, sum(t3.prod_D) as Prod_D, sum(t3.prod_E) as Prod_E  
from
(select t2.name as name, 
case when t2.prodid = 1 then t2.counts
else 0 end  prod_A, 

case when t2.prodid = 2 then t2.counts
else 0 end prod_B,

case when t2.prodid = 3 then t2.counts
else 0 end prod_C,

case when t2.prodid = 4 then t2.counts
else 0 end prod_D, 

case when t2.prodid = "5" then t2.counts
else 0 end prod_E

from 
(SELECT partners.name as name, sales.products_id as prodid, count(products.name) as counts
FROM test.sales left outer join test.partners on sales.partners_id = partners.id
left outer join test.products on sales.products_id = products.id 
where sales.partners_id = partners.id and sales.products_id = products.id group by partners.name, prodid) t2) t3

group by t3.name ;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.