如何在MySQL中返回数据透视表输出?


311

如果我有一个看起来像这样的MySQL表:

company_name操作页数
-------------------------------
公司A打印3
公司A打印2
公司A打印3
公司B电子邮件   
公司B打印2
公司B打印2
公司B打印1
公司A打印3

是否可以运行MySQL查询以获取如下输出:

company_name EMAIL打印1页打印2页打印3页
-------------------------------------------------- -----------
公司A 0 0 1 3
公司B 1 1 2 0

这个想法pagecount可能会有所不同,因此输出列的数量应该反映出这一点,每列action/ 每pagecount对对应一列,然后每列匹配数company_name。我不确定这是否称为数据透视表,但有人建议这样做吗?


3
这称为透视,在SQL之外进行此转换要快得多。
NB

1
Excel会经历这样的事情,在MySQL中确实很困难,因为没有“ CROSSTAB”运算符:(
Dave Rix

是的,它目前是在Excel中手动完成的,我们正在尝试使其自动化。
peku 2011年

3
在这里,我找到了一个逐步的示例:如何自动化数据透视表。而
Devid G

1
@giannischristofakis-这实际上取决于您和您的同事认为更简单的事情。自从我发表评论以来(四年),技术已经流行了很多,因此完全取决于您的感觉更好-是应用程序还是SQL。例如,在我的工作中,我们处理类似的问题,但是我们将SQL和应用程序内方法结合在一起。基本上,除了给出有根据的答案外,我无能为力,那不是您所需要的:)
NB

Answers:


235

这基本上数据透视表。

可以在此处找到有关如何实现此目标的不错的教程: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的东西,好,使用... 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 2014年


1
还有一种不用“ if”,“ case”或“ GROUP_CONCAT”即可生成数据透视表的方法:en.wikibooks.org/wiki/MySQL/Pivot_table
user2513149

您可以从CASE中删除ELSE NULL,因为帽子是默认行为(并且条件聚合足够冗长)
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 2014年

2
“我的解决方案是使用T-SQL的,没有任何枢纽:”不仅SQL Server,它还应在遵循ANSI SQL标准的大多数数据库供应商上运行。请注意,SUM()只能与数字数据的工作,如果你要定义字符串支点,你将不得不使用MAX()
雷蒙德Nijland

1
我认为CASE是不必要的SUM(CASE WHEN (action='PRINT' AND pagecount=1) THEN 1 ELSE 0 END),您可以这样做,SUM(action='PRINT' AND pagecount=1)因为条件将转换为1true和0false时
kajacx

1
@kajacx是的,尽管在没有这种布尔操作的数据库上需要它。考虑到“可以在所有dB上使用的较长语法”和“只能在...上使用的较短语法”之间的选择,我选择前者
Caius Jard

66

对于MySQL,您可以直接将条件放入SUM()函数中,并将其评估为Boolean 01,因此您可以根据条件计算计数,而无需使用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)是否符合标准?
itsols 2014年

3
@itsols仅适用于Mysql专用
-M Khalid Junaid

@itsols:我添加了另一个标准SQL版本。Postgres还具有专用crosstab()功能。
Erwin Brandstetter,2014年

2
也适用于SQLite
SBF

37

对于动态枢轴,请GROUP_CONCAT与一起使用CONCAT。该GROUP_CONCAT函数连接从一组字符串合并为一个字符串的各种选项。

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
佩塞里尔(Pacerier),真正的男人,但对于动态地枢转它是最好的方法之一
Abhishek Gupta

2
如果您在“操作”列中有很多值,或者希望该列表随着时间的推移而增长,则此方法会很好用,因为为每个值编写一个case语句可能很耗时且难以保持最新。
帕特里克·墨菲

23

一个参考标准-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提琴。

怎么样?

TRUE OR NULL 产量TRUE
FALSE OR NULL产量NULL
NULL OR NULL产量NULL
并且COUNT仅计算非空值。Voilá。


@Erwin,但是您怎么知道三列呢?如果有5个怎么办?10个?20吗
Pacerier

@Pacerier:问题中的示例似乎表明了这一点。无论哪种方式,SQL都需要知道返回类型。不可能进行完全动态的查询。如果输出列的数量可以变化,则需要两个步骤:第一个生成查询,第二个执行查询。
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桌子的结构是什么?
帕特里克·墨菲

1
“正确的答案是:”最有可能还不如说是缺少SET查询,以增加其在后1024 GROUP_CONCAT限于1024 GROUP_CONCAT的defualt值简单地截断字符串没有错误的意思意外的结果可能发生..
雷蒙德Nijland

抱歉,人们忘记了更多详细信息。我做一些有趣的事情,然后忘记或破坏了整个项目。但是,当我偶然遇到挑战时,我会分享如何解决它。我知道我的例子不是很详细,但是我想它可能会为那些知道他们所反对的人提供指导:)
Talha

9

有一个称为MySQL Pivot表生成器的工具,它可以帮助您创建基于Web的数据透视表,然后可以将其导出到excel(如果愿意)。如果您的数据在单个表或多个表中,则它可以工作。

您需要做的就是指定列(它支持动态列),rows,表主体中的值和表关系(如果有)的数据源。 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 ;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.