使用pivot

时间:2017-12-27 04:17:24

标签: sql sql-server tsql 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

但是,问题是计数功能不起作用,因为输出错误并且每个月保持显示相同的值。 同时 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)

1 个答案:

答案 0 :(得分:0)

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

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

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

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

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

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

amount

从伪值产生以下输出:

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)

修改

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

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

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

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

请注意列的顺序和名称: ORDER BY

正如您目前所拥有的那样,根据您在查询中选择列的顺序,您将选择count(一个int类型)到month列,将月份名称添加到terminal_name列,将数量添加到pieces列中,并将terminal_name放入金额列。

您将拥有自己独特的风格,但您应该尽量养成正确格式化SQL的习惯。您选择的每一列都应该写在一个单独的行中,并且来自WITH source_table(terminal_name, month_name, pieces, amount) AS ...关键字。 (我的原始帖子中的例外,其中虚拟值联合在一起,并且在一行中选择的所有列都是特殊情况,并且在特定情况下为了清晰起见是妥协!)

所以不是这样:

SELECT

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

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

使用SQL,您将因格式不佳而遭受无尽的痛苦。