使用pivot使用两个聚合函数

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用pivot使用两个聚合函数相关的知识,希望对你有一定的参考价值。

首先,我有查询表结构:

>  SELECT DATENAME(MONTH, sales_timestamp) as MonthName, COUNT(*) as 
>          new_card_qty, ISNULL(sum(nc_deposit),0) as 
>          new_card_total,terminal_name
>          FROM dbfastshosted.dbo.fh_mf_new_card_logs cl
>          INNER JOIN dbfastshosted.dbo.fh_sales_map m on cl.nc_log_id = 
>          m.nc_log_id
>          INNER JOIN dbfastshosted.dbo.fh_sales_logs sl on m.sales_id = 
>          sl.sales_id 
>          INNER JOIN dbfastsconfigdataref.dbo.cdf_terminal_user_account h on 
>          cl.created_user_id = h.terminal_user_id
>          INNER JOIN dbfastsconfigdataref.dbo.cdf_terminal t on h.terminal_id = 
>          t.terminal_id
>          INNER JOIN dbfastsconfigdataref.dbo.cdf_cuid c on cl.cu_id = c.cu_id
>          where YEAR(sales_timestamp)='2017' 
>          and cl.currency_id = 2
>          and c.card_type_id = '514'
>          GROUP BY DATENAME(MONTH, sales_timestamp), DATEPART(MONTH, 
>          sales_timestamp), terminal_name
>          ORDER BY DATEPART(MONTH, sales_timestamp), terminal_name

          MonthName new_card_qty    new_card_total    terminal_name

           January         7               40          Terminal 1
           February        6              55.00        Terminal 2
            March          1              10.00        Terminal 3
            ……..
            ………
            December        7            ....

我希望通过使用Pivot获得这样的结构表,并设法通过查询获取结构:

  SELECT   terminal_name,  
     COUNT(JanuaryPcs) AS JanPcs,SUM(JanuaryAmt) AS JanAmt
    ,COUNT(FebruaryPcs) AS FebPcs,SUM(FebruaryAmt) AS FebAmt
    ,COUNT(MarchPcs) AS MarPcs,SUM(MarchAmt) AS MarAmt
    ,COUNT(AprilPcs) AS AprilPcs,SUM(AprilAmt) AS AprilAmt
    ,COUNT(MayPcs) AS MayPcs,SUM(MayAmt) AS MayAmt
    ,COUNT(JunePcs) AS JunePcs,SUM(JuneAmt) AS JuneAmt
    ,COUNT(JulyPcs) AS JulyPcs,SUM(JulyAmt) AS JulyAmt
    ,COUNT(AugustPcs) AS AugPcs,SUM(AugustAmt) AS AugAmt
    ,COUNT(SeptemberPcs) AS SepPcs,SUM(SeptemberAmt) AS SepAmt
    ,COUNT(OctoberPcs) AS OctPcs,SUM(OctoberAmt) AS OctAmt
    ,COUNT(NovemberPcs) AS NovPcs,SUM(NovemberAmt) AS NovAmt
    ,COUNT(DecemberPcs) AS DecPcs,SUM(DecemberAmt) AS DecAmt
   FROM (SELECT terminal_name,
         DATENAME(month,sales_timestamp)+'Pcs' AS MonthPcs,
         DATENAME(month,sales_timestamp)+'Amt' AS MonthAmt,
          ISNULL(sum(nc_deposit),0) AS AMOUNT,
         ISNULL(COUNT(nc_deposit),0) AS PIECES
   FROM dbfastshosted.dbo.fh_mf_new_card_logs cl
   INNER JOIN dbfastshosted.dbo.fh_sales_map m on cl.nc_log_id = m.nc_log_id
   INNER JOIN dbfastshosted.dbo.fh_sales_logs sl on m.sales_id = sl.sales_id 
   INNER JOIN dbfastsconfigdataref.dbo.cdf_terminal_user_account h on   
   cl.created_user_id = h.terminal_user_id
   INNER JOIN dbfastsconfigdataref.dbo.cdf_terminal t on h.terminal_id = 
   t.terminal_id
   INNER JOIN dbfastsconfigdataref.dbo.cdf_cuid c on cl.cu_id = c.cu_id
   WHERE YEAR(sales_timestamp)='2017' 
   and cl.currency_id = 2
   GROUP BY terminal_name,DATENAME(month,sales_timestamp)+'Pcs' AS MonthPcs,
   DATENAME(month,sales_timestamp)+'Amt') AS monthlysales
   PIVOT
    (COUNT(PIECES) FOR MonthPcs IN (JanuaryPcs,FebruaryPcs,
   MarchPcs,AprilPcs,MayPcs,JunePcs,JulyPcs,AugustPcs,SeptemberPcs, 
   OctoberPcs,NovemberPcs,DecemberPcs)) AS P1
   PIVOT
  (SUM(AMOUNT)FOR MonthAmt IN (JanuaryAmt,FebruaryAmt,MarchAmt,AprilAmt,
   MayAmt,JuneAmt,JulyAmt,AugustAmt, SeptemberAmt,OctoberAmt,
  NovemberAmt,DecemberAmt)) AS P2
  GROUP BY terminal_name;



  terminal_name JanPcs  JanAmt  FebPcs  FebAmt  MarPcs  MarAmt.. DecAmt
        terminal 1        1     10.00    1      20.00     1       30.0

但是,问题是count函数不起作用,因为输出错误并且每个月保持显示相同的值。同时Sum函数成功运行。我尝试了很多次但它不起作用。有什么解决方案吗?

新的更新代码:

WITH source_table(terminal_name, Month_Name, piece, amount) AS
(
SELECT DATENAME(MONTH, sales_timestamp) as Month_Name, COUNT(nc_deposit) as 
          piece, ISNULL(sum(nc_deposit),0) as 
         amount,terminal_name
          FROM dbfastshosted.dbo.fh_mf_new_card_logs cl
          INNER JOIN dbfastshosted.dbo.fh_sales_map m on cl.nc_log_id = 
          m.nc_log_id
         INNER JOIN dbfastshosted.dbo.fh_sales_logs sl on m.sales_id = 
          sl.sales_id 
          INNER JOIN dbfastsconfigdataref.dbo.cdf_terminal_user_account h on 
          cl.created_user_id = h.terminal_user_id
          INNER JOIN dbfastsconfigdataref.dbo.cdf_terminal t on h.terminal_id = 
         t.terminal_id
          INNER JOIN dbfastsconfigdataref.dbo.cdf_cuid c on cl.cu_id = c.cu_id
        where YEAR(sales_timestamp)='2017' 
          and cl.currency_id = 1
          and c.card_type_id = '514'
         GROUP BY DATENAME(MONTH, sales_timestamp), MONTH(sales_timestamp),terminal_name
         ORDER BY MONTH(sales_timestamp), terminal_name             
)
Select   
    pieces.terminal_name, 
    pieces.January AS JanPcs,
    amounts.January AS JanAmt,
    pieces.February AS FebPcs,
    amounts.February AS FebAmt,
    pieces.March AS MarPcs,
    amounts.March AS MarAmt,
    pieces.April AS AprilPcs,
    amounts.April AS AprilAmt,
    pieces.May AS MayPcs,
    amounts.May AS MayAmt,
    pieces.June AS JunePcs,
    amounts.June AS JuneAmt,
    pieces.July AS JulyPcs,
    amounts.July AS JulyAmt,
    pieces.August AS AugustPcs,
    amounts.August AS AugustAmt,
    pieces.September AS SeptPcs,
    amounts.September AS SeptAmt,
    pieces.October AS OctPcs,
    amounts.October AS OctAmt,
    pieces.November AS NovPcs,
    amounts.November AS NovAmt, 
    pieces.December AS DecPcs,
    amounts.December AS DecAmt
FROM
    (
        SELECT
            *
        FROM
            (
              SELECT
                    terminal_name
                    ,Month_Name
                    ,piece
                FROM
                    source_table

            ) AS src_pieces
        PIVOT
            (
                MAX(piece)
                FOR Month_Name IN (January, February, March, April, May, June, July, August, September, October, November, December)
            ) AS pvt_pieces

    ) AS pieces

INNER JOIN 
    (
        SELECT
            *

        FROM
            (
                SELECT
                    terminal_name
                    ,Month_Name
                    ,amount
                FROM
                    source_table

            ) AS src_pieces
        PIVOT
            (
                MAX(amount)
                FOR Month_Name IN (January, February, March, April, May, June, July, August, September, October, November, December)
            ) AS pvt_pieces
    ) AS amounts
 ON (amounts.terminal_name = pieces.terminal_name)
答案

数据透视表中的COUNT(pieces)计算将汇总到该特定单元格中的非空值的数量 - 所有列的值都为1,因为这是从该卷中汇总的非空值的数量。基本查询到透视字段。

我建议你解决这个问题的方法是首先编写一个查询(最好使用WITH子句),使用以下列正确生成一个表(标准的,不透明的形式):terminal_namemonth_namepiecesamount

当我说标准表格时,我的意思是每个终端应该有12行与之相关联(每月一个),而且数量和金额列应该是该终端和该月份的相关聚合值。你的代码的逻辑有点不清楚,所以我不会尝试为你做。

一旦你有了这个排序,你只使用MAX聚合做两个独立的枢轴(因为SUMs和COUNTs已经在被转动的源表中预先完成 - 我们只使用MAX来选择单个非空值来自源表,因为它只包含终端和月份的每个组合的一个值)。

然后,一个pivot生成amount列,另一个生成count列,然后在select语句中将它们交错,方式与您已经完成的方式相同。

我在下面汇总了一个工作示例。我不知道我是否可以使用语法更加经济,因为我之前从未需要进行双重旋转,但我遵循了一个我知道正常工作的模式。

WITH source_table(terminal_name, month_name, pieces, amount) AS
(
    --PUT YOUR YOUR QUERY HERE INSTEAD OF THESE DUMMY VALUES
    SELECT 'Term1', 'Jan', 1, 10.00
    UNION ALL
    SELECT 'Term1', 'Feb', 5, 20.00
    UNION ALL
    SELECT 'Term1', 'Mar', 6, 30.00
    UNION ALL
    SELECT 'Term2', 'Jan', 7, 40.00
)

SELECT
    pieces.terminal_name

    ,pieces.Jan AS JanPcs
    ,amounts.Jan AS JanAmt

    ,pieces.Feb AS FebPcs
    ,amounts.Feb AS FebAmt

    ,pieces.Mar AS MarPcs
    ,amounts.Mar AS MarAmt

FROM
    (
        SELECT
            *

        FROM
            (
                SELECT
                    terminal_name
                    ,month_name
                    ,pieces
                FROM
                    source_table

            ) AS src_pieces
        PIVOT
            (
                MAX(pieces)
                FOR month_name IN (Jan, Feb, Mar)
            ) AS pvt_pieces

    ) AS pieces

INNER JOIN 
    (
        SELECT
            *

        FROM
            (
                SELECT
                    terminal_name
                    ,month_name
                    ,amount
                FROM
                    source_table

            ) AS src_pieces
        PIVOT
            (
                MAX(amount)
                FOR month_name IN (Jan, Feb, Mar)
            ) AS pvt_pieces

    ) AS amounts
    ON (amounts.terminal_name = pieces.terminal_name)

这会从虚拟值产生以下输出:

terminal_name JanPcs      JanAmt   FebPcs      FebAmt   MarPcs      MarAmt
------------- ----------- -------- ----------- -------- ----------- --------
Term1         1           10.00    5           20.00    6           30.00
Term2         7           40.00    NULL        NULL     NULL        NULL

编辑:

根据以下评论和发布的更新代码。

我马上注意到你仍然在with-block中有ORDER BY子句。在这种情况下,order-by子句应该在查询的最底部之后(在整个事物的最后一行,在两个数据透视表之间的连接之后)。

我花了10分钟的时间仔细检查,发现你在with-block中选择了错误顺序的列。

应首先选择terminal_name,然后选择月份,然后选择件数,然后选择金额(以匹配带有块的列名称)。

请注意列中的顺序和名称:WITH source_table(terminal_name, month_name, pieces, amount) AS ...

正如您目前所拥有的那样,根据您在查询中选择列的顺序,您将在month列中选择count(一个int类型),在terminal_name列中选择月份名称,在pieces列中选择数量,以及terminal_name进入amount列。

您将拥有自己独特的风格,但您应该尝试尽可能一致地正确格式化SQL。您选择的每一列都应该写在一个单独的行上,并且来自SELECT关键字。 (在我的原始帖子中将虚拟值联合在一起的例外情况,以及在一行中选择的所有列都是一种特殊情况,并且在特定情况下为了清晰起见是妥协!)

所以不是这样:

SELECT DATENAME(MONTH, sales_timestamp) as Month_Name, COUNT(nc_deposit) as 
      piece, ISNULL(sum(nc_deposit),0) as 
     amount,terminal_name
      FROM ...

它应该看起来像这样(根据上面的列顺序更正):

SELECT
    terminal_name
    ,DATENAME(MONTH, sales_timestamp) AS Month_Name
    ,COUNT(nc_deposit)                AS piece
    ,ISNULL( SUM(nc_deposit), 0 )     AS amount

FROM ...

使用SQL,您将因糟糕的格式化而遭受无尽的痛苦。

以上是关于使用pivot使用两个聚合函数的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL Pivot 上使用 Min 聚合

没有聚合函数的 TSQL Pivot

具有动态生成列、聚合函数和无聚合列的 SQL Pivot

同一列上具有多个聚合的 T-SQL Pivot

PIVOT 没有聚合。在多列上使用 Max

在数据透视聚合函数中使用CONCAT函数的SQL Server错误