在 CTE 中对日期范围内的列求和?

Posted

技术标签:

【中文标题】在 CTE 中对日期范围内的列求和?【英文标题】:Summing a column over a date range in a CTE? 【发布时间】:2016-04-12 16:51:33 【问题描述】:

我正在尝试对某个日期范围内的某个列求和。更重要的是我希望这是一个 CTE,因为我必须多次使用它作为更大查询的一部分。由于它是 CTE,它必须具有日期列以及 sum 和 ID 列,这意味着我必须按日期和 ID 分组。这将导致我的结果按 ID 和日期分组,给我的不是一个日期范围内的总和,而是一堆总和,每天一个。

为了简单起见,假设我们有:

create table orders (  
 id int primary key,  
 itemID int foreign key references items.id,  
 datePlaced datetime,  
 salesRep int foreign key references salesReps.id,  
 price int,  
 amountShipped int);

现在,我们想要获取给定销售代表在一个财政年度内的总收入,按项目细分。也就是说,忽略会计年度位:

select itemName, sum(price) as totalSales, sum(totalShipped) as totalShipped
 from orders
 join items on items.id = orders.itemID
 where orders.salesRep = '1234'
 group by itemName

足够简单。但是,当您添加任何其他内容(甚至是价格)时,查询会吐出比您想要的更多的行。

    select itemName, price, sum(price) as totalSales, sum(totalShipped) as totalShipped
     from orders
 join items on items.id = orders.itemID
 where orders.salesRep = '1234'
 group by itemName, price

现在,每个组都是(名称,价格)而不仅仅是(名称)。这是一种 sudocode,但在我的数据库中,只是这个更改导致我的结果集从 13 行跳到 32 行。再加上日期范围,你真的有问题:

select itemName, price, sum(price) as totalSales, sum(totalShipped) as totalShipped  
 from orders  
 join items on items.id = orders.itemID  
 where orders.salesRep = '1234'  
  and orderDate between 150101 and 151231  
 group by itemName, price  

这与上一个示例相同。问题是让它成为 CTE:

with totals as (  
 select itemName, price, sum(price) as totalSales, sum(totalShipped) as totalShipped, orderDate as startDate, orderDate as endDate  
  from orders  
   join items on items.id = orders.itemID  
  where orders.salesRep = '1234'  
   and orderDate between startDate and endDate  
 group by itemName, price, startDate, endDate  
)  
select totals_2015.itemName as itemName_2015, totals_2015.price as price_2015, ...  
 totals_2016.itemName as itemName_2016, ...  
 from (  
 select * from totals  
 where startDate = 150101 and endDate = 151231  
) totals_2015  
 join (  
 select *  
 from totals  
 where startDate = 160101 and endDate = 160412  
) totals_2016  
on totals_2015.itemName = totals_2016.itemName  

现在 CTE 中的分组已经很遥远了,不仅仅是添加价格。我曾考虑将价格查询分解为 CTE 中自己的子查询,但我无法避免需要按日期分组以获得日期范围。任何人都可以看到解决这个问题的方法吗?我希望我已经把事情说得够清楚了。这是针对 IBM iSeries 机器运行的。谢谢!

【问题讨论】:

你为什么认为Since it's a CTE, it has to have the date column是真的? 抱歉,我没有说清楚。与上一个示例一样,CTE 将用于获取多年。通过这种方式,相同的 CTE 可用于获得两年、三年、四年或更长时间,只需为其指定不同的日期即可。这将输出到网站上的表格中,每个 itemName 一行,列数为年,乘以(价格、totalShipped、totalSales)的计数。 也许临时表或视图是比 cte 更好的方法。顺便说一句,如果您花时间和精力正确地格式化您的问题,即,将您的代码格式化为代码,您更有可能获得帮助。 如果我使用临时表,难道我还没有必须按日期分组的问题吗?如前所述,这个想法是这个“总数”将在单个查询中使用两次或更多次。关于格式,谢谢提醒。现在应该看起来更好了。 一个包含日期和财政年度的临时表让我觉得这是一个好方法。 【参考方案1】:

根据您要查找的内容,这可能是更好的方法:

select 'by sales rep' breakdown
, salesRep
, '' year
, sum(price * amountShipped) amount
from etc
group by salesRep

union

select 'by sales rep and year' breakdown
, salesRep
, convert(char(4),orderDate, 120)  year
, sum(price * amountShipped) amount
from etc
group by salesRep, convert(char(4),orderDate, 120)

etc

【讨论】:

对不起,我没有按照这个例子。我如何将总和限制在某个日期范围内,这不会给我重复的行吗?或者至少每个结果有两行?【参考方案2】:

如果可能按 id 列或外键分组,因为这些列已编入索引,您将获得更快的结果。这适用于任何数据库。

with cte as (
select id,rep, sum(sales) sls, count(distinct itemid) did, count(*) cnt from sommewhere 
where date between x and y
group by id,rep
) select * from cte order by rep

或者更花哨

with cte as (
select id,rep, sum(sales) sls, count(distinct itemid) did, count(*) cnt from sommewhere 
where date between x and y
group by id,rep
) select * from cte join reps on cte.rep = reps.rep order by sls desc

【讨论】:

感谢您的回答,但您在 CTE 中硬编码了日期。我的问题是我希望能够使用 CTE 在任何两个日期之间获得这些总和。这意味着在选择中放置两个日期列,因为 CTE 只能通过选择日期来指定日期,这意味着按日期分组。除非我严重误解了某些东西(很有可能)? 您在运行时填写 x 和 y 您可以在 CTE 块中按 id、rep、year(date)、month(date) 分组。去吧,你可以做到的。但请始终记住,group by 具有构建在其之上的键作为 group by 子句中的第一个元素,并且您的查询将运行得很快 我使用 CTE 来避免在运行时进行调整。例如,我想调用它两次,一次用于一个日期范围,一次用于另一个日期范围。或者这可能最终会提供一个网页,所以日期必须来自一个表格。使用表单会很容易,因为我可以替换值,但我仍然不能多次使用它。 取出 x 和 y 并在 group by 和 selected 列中添加 year(date) 、 month(date) 。去吧。 按月分组,这很好。我仍然没有看到答案的是 CTE 如何获取传递给它的日期范围。如果我想要 4 月到 7 月,我必须以某种方式告诉 CTE。唯一的方法是说“cte.date 在 x 和 y 之间的位置”,但这意味着在 CTE 的选择中放置一个“日期”。那么,这意味着我必须按日期列进行分组,而不仅仅是日期的年份和月份。还是我错了?我可能错了。【参考方案3】:

我最终找到了一个解决方案,它根本不需要 CTE。我希望 CTE 避免代码重复,但这几乎同样有效。这是一个thread explaining summing conditionally,正是我想要的。

【讨论】:

以上是关于在 CTE 中对日期范围内的列求和?的主要内容,如果未能解决你的问题,请参考以下文章

仅对 Google 表格中某个范围内的正数/负数求和

统计日期范围内的天数

Excel 自动填充和调整公式

SQL - 按日期范围查询

javascript 求和范围内的所有数字

如何从单个表中对多行进行分组并获取给定范围内的所有记录