2

有没有办法在 SQL Server 中编写这样的查询,而不使用两次选择然后加入?

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 添加到分组依据,并在我这样做时返回空值

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
4

3 回答 3

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上的演示

于 2013-04-08T08:53:25.443 回答
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 )

您可以在没有 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
于 2013-04-08T09:07:44.813 回答
2

您也可以尝试使用UNPIVOTPIVOT如下所示:

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
  1. preparedCTE 生成以下列集:

    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四舍五入到最接近的半小时totalprice乘以amount

  2. unpivotedCTE 将andtotal值反旋转amount以生成attributeand 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
    
  3. 最后,pivotedCTE 将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
    

有几个问题可能需要解决:

  1. 如果priceamount是不同的数据类型,则totalamount也可能以不同的数据类型结束。对于UNPIVOT,未透视的值必须具有完全相同的类型,因此您需要将totaland显式转换amount为某些常见类型,可能会防止数据/精度丢失。这可以prepared像这样在 CTE 中完成(假设通用类型为decimal(10,2)):

    total  = CAST(price * amount AS decimal(10,2)),
    amount = CAST(amount AS decimal(10,2))
    
  2. 如果合计金额最终可能为 0,则您需要考虑除以 0 问题。一种方法是用 NULL 替换 0 数量,这将使除法的结果也为 NULL。将ISNULLorCOALESCE应用于该结果将允许您将其转换为某个默认值,例如 0。pivoted因此,在CTE中更改此位:

    avgA = ISNULL(total11111 * 100 / NULLIF(amount11111, 0), 0),
    avgB = ISNULL(total22222 * 100 / NULLIF(amount22222, 0), 0)
    
于 2013-04-09T16:03:27.427 回答