有没有办法在SQL Server中编写这样的查询,而不使用select两次然后加入?
select trans_date, datepart(HOUR,trans_time) as hour,
(datepart(MINUTE,trans_time)/30)*30 as minute,
case
when paper_number = 11111/*paperA*/
then sum(t1.price*t1.amount)/SUM(t1.amount)*100
end as avgA,
case
when paper_number = 22222/*PaperB*/
then sum(t1.price*t1.amount)/SUM(t1.amount)*100
end as avgB
from dbo.transactions t1
where trans_date = '2006-01-01' and (paper_number = 11111 or paper_number = 22222)
group by trans_date, datepart(HOUR,trans_time), datepart(MINUTE,trans_time)/30
order by hour, minute
SQL Server要求我将paper_number添加到group by,并在我这样做时返回null
trans_date hour minute avgA avgB
2006-01-01 9 30 1802.57199725463 NULL
2006-01-01 9 30 NULL 169125.886524823
2006-01-01 10 0 1804.04742534103 NULL
2006-01-01 10 0 NULL 169077.777777778
2006-01-01 10 30 1806.18773535637 NULL
2006-01-01 10 30 NULL 170274.550381867
2006-01-01 11 0 1804.43466045433 NULL
2006-01-01 11 0 NULL 170743.4
2006-01-01 11 30 1807.04532012137 NULL
2006-01-01 11 30 NULL 171307.00280112
答案 0 :(得分:3)
对整个CASE表达式使用SUM()函数
select trans_date, datepart(HOUR,trans_time) as hour, (datepart(MINUTE,trans_time)/30)*30 as minute,
sum(case when paper_number = 11111/*paperA*/ then t1.price*t1.amount end) * 1.00
/ sum(case when paper_number = 11111/*paperA*/ then t1.amount end) * 100 as avgA,
sum(case when paper_number = 22222/*PaperB*/ then t1.price*t1.amount end) * 1.00
/ sum(case when paper_number = 22222/*paperB*/ then t1.amount end) * 100 as avgB
from dbo.transactions t1
where trans_date = '2006-01-01'
group by trans_date, datepart(HOUR,trans_time), datepart(MINUTE,trans_time)/30
order by hour, minute
SQLFiddle上的演示
答案 1 :(得分:3)
尝试:
with cte as
(select trans_date,
datepart(HOUR,trans_time) as hour,
(datepart(MINUTE,trans_time)/30)*30 as minute,
sum(case when paper_number = 11111/*paperA*/
then t1.price*t1.amount else 0 end) as wtdSumA,
sum(case when paper_number = 11111/*paperA*/
then t1.amount else 0 end) as amtSumA,
sum(case when paper_number = 22222/*PaperB*/
then t1.price*t1.amount else 0 end) as wtdSumB,
sum(case when paper_number = 22222/*PaperB*/
then t1.amount else 0 end) as amtSumB
from dbo.transactions t1
where trans_date = '2006-01-01'
group by trans_date, datepart(HOUR,trans_time), datepart(MINUTE,trans_time)/30)
select trans_date, hour, minute,
case amtSumA when 0 then 0 else 100 * wtdSumA / amtSumA end as avgA,
case amtSumB when 0 then 0 else 100 * wtdSumB / amtSumB end as avgB
from cte
order by hour, minute
(SQLFiddle here)
你可以在没有CTE的情况下得出这个,如下:
select trans_date,
datepart(HOUR,trans_time) as hour,
(datepart(MINUTE,trans_time)/30)*30 as minute,
case sum(case when paper_number = 11111/*paperA*/ then t1.amount else 0 end)
when 0 then 0
else 100 * sum(case when paper_number = 11111 then t1.price*t1.amount else 0 end)
/ sum(case when paper_number = 11111 then t1.amount else 0 end) end as avgA,
case sum(case when paper_number = 22222/*paperA*/ then t1.amount else 0 end)
when 0 then 0
else 100 * sum(case when paper_number = 22222 then t1.price*t1.amount else 0 end)
/ sum(case when paper_number = 22222 then t1.amount else 0 end) end as avgB
from dbo.transactions t1
where trans_date = '2006-01-01'
group by trans_date, datepart(HOUR,trans_time), datepart(MINUTE,trans_time)/30
order by 1,2,3
答案 2 :(得分:2)
您也可以尝试使用UNPIVOT
和PIVOT
,如下所示:
WITH prepared AS (
SELECT
trans_date,
trans_time = DATEADD(MINUTE, DATEDIFF(MINUTE, '00:00', trans_time) / 30 * 30, CAST('00:00' AS time)),
paper_number,
total = price * amount,
amount
FROM transactions
),
unpivoted AS (
SELECT
trans_date,
trans_time,
attribute = attribute + CAST(paper_number AS varchar(10)),
value
FROM prepared
UNPIVOT (value FOR attribute IN (total, amount)) u
),
pivoted AS (
SELECT
trans_date,
trans_time,
avgA = total11111 * 100 / amount11111,
avgB = total22222 * 100 / amount22222
FROM unpivoted
PIVOT (
SUM(value) FOR attribute IN (total11111, amount11111, total22222, amount22222)
) p
)
SELECT *
FROM pivoted
;
为了解释上述查询的工作原理,下面是对原始数据集在查询执行过程中经历的转换的描述,使用以下示例:
trans_date trans_time paper_number price amount
---------- ---------- ------------ ----- ------
2013-04-09 11:12:35 11111 10 15
2013-04-09 11:13:01 22222 24 10
2013-04-09 11:28:44 11111 12 5
2013-04-09 11:36:20 22222 20 11
prepared
CTE生成以下列集:
trans_date trans_time paper_number total amount
---------- ---------- ------------ ----- ------
2013-04-09 11:00:00 11111 150 15
2013-04-09 11:00:00 22222 240 10
2013-04-09 11:00:00 11111 60 5
2013-04-09 11:30:00 22222 220 11
其中trans_time
为原始trans_time
向下舍入到最近的半小时,total
为price
乘以amount
。
unpivoted
CTE忽略了total
和amount
值,以生成attribute
和value
:
trans_date trans_time paper_number attribute value
---------- ---------- ------------ --------- -----
2013-04-09 11:00:00 11111 total 150
2013-04-09 11:00:00 11111 amount 15
2013-04-09 11:00:00 22222 total 240
2013-04-09 11:00:00 22222 amount 10
2013-04-09 11:00:00 11111 total 60
2013-04-09 11:00:00 11111 amount 5
2013-04-09 11:30:00 22222 total 220
2013-04-09 11:30:00 22222 amount 11
然后paper_number
与attribute
合并形成一个列,也称为attribute
:
trans_date trans_time attribute value
---------- ---------- ----------- -----
2013-04-09 11:00:00 total11111 150
2013-04-09 11:00:00 amount11111 15
2013-04-09 11:00:00 total22222 240
2013-04-09 11:00:00 amount22222 10
2013-04-09 11:00:00 total11111 60
2013-04-09 11:00:00 amount11111 5
2013-04-09 11:30:00 total22222 220
2013-04-09 11:30:00 amount22222 11
最后,pivoted
CTE将value
数据转发回来,并使用SUM()
并使用attribute
列名称进行聚合:
trans_date trans_time total11111 amount11111 total22222 amount22222
---------- ---------- ---------- ----------- ---------- -----------
2013-04-09 11:00:00 210 20 240 10
2013-04-09 11:30:00 NULL NULL 220 11
然后另外处理旋转的值(每totalNNN
乘以100并除以相应的amountNNN
)以形成最终输出:
trans_date trans_time avgA avgB
---------- ---------- ---- ----
2013-04-09 11:00:00 1050 2400
2013-04-09 11:30:00 NULL 2000
可能需要解决几个问题:
如果price
和amount
是不同的数据类型,则total
和amount
也可能会以不同的数据类型结束。对于UNPIVOT
,要取消隐藏的值必须具有完全相同的类型,因此您需要将total
和amount
的显式转换添加到一些常见的类型,可能会阻止数据/精度损失。这可以在这样的prepared
CTE中完成(假设共同类型为decimal(10,2)
):
total = CAST(price * amount AS decimal(10,2)),
amount = CAST(amount AS decimal(10,2))
如果汇总金额可能最终为0,那么您需要将除以0的问题考虑在内。一种方法可以是用NULL替换0量,这也会使除法的结果为NULL。将ISNULL
或COALESCE
应用于该结果将允许您将其转换为某个默认值,例如0。因此,在pivoted
CTE:
avgA = ISNULL(total11111 * 100 / NULLIF(amount11111, 0), 0),
avgB = ISNULL(total22222 * 100 / NULLIF(amount22222, 0), 0)