使用具有不同金额的相同密钥的两个表进行聚合

时间:2016-07-24 07:10:11

标签: sql count case inner-join aggregate

其他示例无法提供适合我情况的解决方案。

假设我有两个表Transactions和Budget。两者都有相同的类别名称,如“抵押贷款”,“保险”,“汽车付款”。但是,预算表每个类别只有1行作为总和,而Transactions表可以有匹配类别的x行。如何生成一个表格,以便我有样本参数@Year = 2016和@Month = 5:

 [Category] [Month to Date Transactions Amount] [Month to Date Budget Amount]
  Mortgage            1000                             1000                           
  Insurance            300                             300
  Car Payment          600                             600

鉴于以下表格:

交易

 [Transaction_Date]    [Category]    [Amount]
20160504                Mortgage       500
20160524                Mortgage       500
20160510                Insurance      300
20160501                Car Payment    200
20160515                Car Payment    200
20160531                Car Payment    200

预算

 [Transaction_Date]    [Category]    [Amount]
20160501                Mortgage       1000
20160501                Insurance      300
20160501                Car Payment    600

在这种情况下,预算表始终使用第一天作为默认值。

当我使用Sum(Case when ...)语句预先形成聚合时,我的查询中的[月末预算金额]似乎是事务表中行数量的一个因子。使用SUM(Case when ...)语句,我尝试将[月份预算金额]列除以COUNT(Case when ...)语句但我收到了“无法执行聚合函数包含聚合或子查询的表达式。“错误?可以这样做吗?我的示例代码如下:

 declare @Year as integer = 2016
 Declare @Month as integer = 5

 SELECT
 Transactions.Category
 ,ISNULL(SUM(CASE WHEN 
 Year(Transactions.Transaction_Date) = @Year and 
 Month(Transactions.Transaction_Date) = @Month and
 Year(Budget.Transaction_Date) = @Year and
 Month(Budget.Transaction_Date) = @Month
 THEN Transactions.Amount END),0) AS [Month to Date Transactions Amount]
 ,ISNULL(SUM(CASE WHEN 
 Year(Transactions.Transaction_Date) = @Year and 
 Month(Transactions.Transaction_Date) = @Month and
 Year(Budget.Transaction_Date) = @Year and
 Month(Budget.Transaction_Date) = @Month
 THEN Budget.Amount END),0) AS [Month to Date Budget Amount]

 FROM
   Transactions
   INNER JOIN Category
     ON Transactions.Category = Budget.Category
 GROUP BY
   Transactions.Category

输出

      [Category] [Month to Date Transactions Amount] [Month to Date  Budget Amount]
      Mortgage            1000                             2000                           
      Insurance            300                             300
      Car Payment          600                             1800  

正如我所观察到的,由于交易中有两个抵押行,我们对预算汇总有双重计算;与汽车付款一样,我们有3行,因此预算汇总是三重计算。

提前感谢所有回复的人。

2 个答案:

答案 0 :(得分:0)

这样做的方法不止一种。只需将第二个“SUM”切换为“MAX”,或者,例如,写下这样的内容:

declare @Year as integer = 2016
declare @Month as integer = 5

select t.category, t.amount, b.amount
from
  (select category, sum(amount) as amount
  from Transactions
  where year([Transaction_Date]) = @Year
  and month([Transaction_Date]) = @Month
  group by category) t
inner join Budget b
on t.category = b.category
and year(b.[Transaction_Date]) = @Year
and month(b.[Transaction_Date]) = @Month

然后添加所需的列名称。

或者,根据您使用变量的确切原因,您可以使用:

select t.year, t.month, t.category, t.amount as transaction_amount, b.amount as budget_amount
from
  (select year(Transaction_Date) as year, month(Transaction_Date) as month, category, sum(amount) as amount
  from Transactions
  group by year(transaction_date), month(transaction_date), category) t
inner join Budget b
  on t.year = year(b.transaction_date)
  and t.month = month(b.transaction_date)
  and t.category = b.category
-- where t.year = @Year and t.month = @Month
order by t.year, t.month, t.category

您当然可以使用where子句限制年份和月份,然后删除年份和月份列以恢复原始要求。

对评论的回应:

select t.category, t.amount as transaction_amount, b.amount as budget_amount
from
  (select category, sum(amount) as amount
  from Transactions
  where year([Transaction_Date]) = @Year
  group by category) t
inner join 
  (select category, sum(amount) as amount
  from Budget
  where year([Transaction_Date]) = @Year
  group by category) b
on t.category = b.category

答案 1 :(得分:0)

在下面的SQL中,请注意当前期间和下一期间的开始日期。此用法是一种标准SARG-able模式,确保在可用时使用索引来优化磁盘访问;并且如果te列类型实际上是日期时间而不是日期,则不会省略或重复计算任何rtansactions。

declare @Year      as integer   = 2016
      , @Month     as integer   = 5;

declare @thisPdDate as date     = datefromParts(@year,@month,1);
declare @nextPdDate as date     = dateadd(day,1,eomonth(@thisPdDate));

select t.category, t.amount, b.amount
from (
  select category, sum(amount) as amount
  from Transactions
  where @thisPdDate  <= Transaction_Date and Transaction_Date < @nextPdDate
  group by category
) t
inner join Budget b
   on t.category    = b.category
where @thisPdDate  <= b.Transaction_Date and b.Transaction_Date < @nextPdDate

这些常见的用法(在WHERE或JOIN子句中)不能SARG 并且需要表扫描:

  • 年(Transaction_Date)
  • 月(TRANSACTION_DATE)

另外,请注意,使用BETWEEN ... AND ...进行日期查找是一种反模式,除非可以绝对保证列类型将是 date 而不是< EM>日期时间。如果存在任何疑问,请使用上述模式而不是BETWEEN ... AND ....