在 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 中对日期范围内的列求和?的主要内容,如果未能解决你的问题,请参考以下文章