使用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_name
,month_name
,pieces
,amount
。
当我说标准表格时,我的意思是每个终端应该有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使用两个聚合函数的主要内容,如果未能解决你的问题,请参考以下文章