每列的条件不同

时间:2013-04-08 08:35:13

标签: sql sql-server sql-server-2008

有没有办法在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

3 个答案:

答案 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)

您也可以尝试使用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. 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向下舍入到最近的半小时,totalprice乘以amount

    < / LI>
  2. unpivoted CTE忽略了totalamount值,以生成attributevalue

    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_numberattribute合并形成一个列,也称为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. 最后,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
    
  4. 可能需要解决几个问题:

    1. 如果priceamount是不同的数据类型,则totalamount也可能会以不同的数据类型结束。对于UNPIVOT,要取消隐藏的值必须具有完全相同的类型,因此您需要将totalamount的显式转换添加到一些常见的类型,可能会阻止数据/精度损失。这可以在这样的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。将ISNULLCOALESCE应用于该结果将允许您将其转换为某个默认值,例如0。因此,在pivoted CTE:

      中更改此位
      avgA = ISNULL(total11111 * 100 / NULLIF(amount11111, 0), 0),
      avgB = ISNULL(total22222 * 100 / NULLIF(amount22222, 0), 0)