【问题标题】:SQL Query for Date Range, multiple start/end times日期范围、多个开始/结束时间的 SQL 查询
【发布时间】:2011-03-18 16:41:25
【问题描述】:

Microsoft SQL Server 中存在一个表,其中包含记录 ID、开始日期、结束日期和数量。

这个想法是,对于每条记录,数量/范围内的总天数 = 每日数量。

鉴于存在包含所有可能日期的表,我如何在 SQL Server 中生成类似于以下示例的结果集?

前:

RecordID | Start Date | End Date  | Quantity
1        |  1/1/2010  | 1/5/2010  | 30000
2        |  1/3/2010  | 1/9/2010  | 20000
3        |  1/1/2010  | 1/7/2010  | 10000

Results as
1        | 1/1/2010 |  QTY (I can do the math easy, just need the dates view)
1        | 1/2/2010 | 
1        | 1/3/2010 | 
1        | 1/4/2010 | 
1        | 1/3/2010 | 
2        | 1/4/2010 | 
2        | 1/5/2010 | 
2        | 1/6/2010 | 
2        | 1/7/2010 | 
2        | 1/8/2010 | 
2        | 1/9/2010 | 
3        | 1/1/2010 | 
3        | 1/2/2010 | 
3        | 1/3/2010 | 
3        | 1/4/2010 | 
3        | 1/5/2010 | 
3        | 1/6/2010 | 
3        | 1/7/2010 | 

按我可以得到的日期分组,然后得到当天的数量总和,但由于用户提供的过滤器可能会排除其中一些记录,因此最终结果集无法汇总。

编辑

为了澄清,这只是一个示例。过滤器无关紧要,因为我可以加入到一边以在结果中提取与记录 ID 相关的详细信息。

真实数据包含 N 条记录,每周增加,日期永远不会相同。可能有 2000 条记录具有不同的开始日期和结束日期......这就是我想要为其生成视图的内容。我可以直接加入数据来完成我需要的其余部分

我还应该提到这是针对过去、现在和未来的数据。我很想摆脱一个临时的日期表。我正在使用递归查询来获取 50 年内存在的所有日期,但这超出了视图的 MAXRECURSION 限制,我无法使用。

【问题讨论】:

  • 我不确定我是否了解有关用户提供的过滤器的信息。能详细点吗?
  • 过滤器真的无关紧要。我将把这个列表加入到包含数量等信息的主列表中,并且可以使用这些事实来得出全面的值

标签: sql-server date-range


【解决方案1】:

我想你可以试试这个。

SELECT [Quantities].[RecordID], [Dates].[Date], SUM([Quantity])
FROM [Dates]
JOIN [Quantities] on [Dates].[Date] between [Quantities].[Start Date] and [End Date]
GROUP BY [Quantities].[RecordID], [Dates].[Date]
ORDER BY [Quantities].[RecordID], [Dates].[Date]

【讨论】:

  • 美丽。我没想到在我的加入操作中使用 between 。我仍然有这个我想删除的临时日期表,但如果不能这样做,这是解决问题的一个干净的解决方案
【解决方案2】:

回答

select RecordId,d.[Date], Qty/ COUNT(*) OVER (PARTITION BY RecordId) AS Qty
from EX join Dates d on d.Date between [Start Date] and [End Date]
ORDER BY RecordId,[Date]

注意:以下演示 CTE 使用 SQL Server 2008 的 date 数据类型,但一般方法也适用于 SQL2005。

测试用例

/*CTEs for testing purposes only*/

WITH EX AS
(
    SELECT 1 AS RecordId, 
    cast('1/1/2010' as date) as [Start Date], 
    cast('1/5/2010' as date) as  [End Date], 
    30000 AS Qty
union all
    SELECT 2 AS RecordId, 
    cast('1/3/2010' as date) as [Start Date], 
    cast('1/9/2010' as date) as  [End Date], 
    20000  AS Qty
),Dates AS /*Dates Table now adjusted to do greater range*/
(

SELECT  DATEADD(day,s1.number + 2048*s2.number,'1990-01-01') AS [Date] 
FROM master.dbo.spt_values s1 CROSS JOIN master.dbo.spt_values s2
where s1.type='P' AND s2.type='P' and s2.number <= 8
order by  [Date] 
)


select RecordId,d.[Date], Qty/ COUNT(*) OVER (PARTITION BY RecordId) AS Qty
from EX join Dates d on d.Date between [Start Date] and [End Date]
ORDER BY RecordId,[Date]

结果

RecordId    Date       Qty
----------- ---------- -----------
1           2010-01-01 6000
1           2010-01-02 6000
1           2010-01-03 6000
1           2010-01-04 6000
1           2010-01-05 6000
2           2010-01-03 2857
2           2010-01-04 2857
2           2010-01-05 2857
2           2010-01-06 2857
2           2010-01-07 2857
2           2010-01-08 2857
2           2010-01-09 2857

【讨论】:

  • 我没有提到这是针对过去、现在和未来的数据。我喜欢这样的想法,即我不必从临时表中获取日期,但不幸的是,这可能会持续两年,并且针对这些结果使用此查询不会为未来的数据产生任何结果。不过,我很想摆脱这个临时日期表......
  • @Mohgeroth - 带有日期索引的专用日期表将是最有效的解决方案。您应该能够轻松调整我的查询以查看它而不是我的演示日期表。
  • “未来日期”是什么意思?他的查询适用于长达 22 年以上的跨度,无论是过去还是未来。如果您的意思是希望在列表中查看数量为 0 的未来日期,例如“2011-01-01 0”,那么您必须包含一个指定开始和结束总日期范围的参数。
  • @Mohgeroth 但是话虽如此,如果你真的想拍它,我已经调整了我的答案,所以它适用于 1990 年 1 月 1 日至 2040 年 6 月 18 日之间的日期。显然你可能需要调整这些日期是你的结束日期。
  • 是的,我没有足够的时间来看看它在做什么,这是一个很好的答案。我对 spt_values 表了解不多,需要一些时间才能弄清楚它代表什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-12-16
  • 2020-03-21
  • 1970-01-01
  • 1970-01-01
  • 2022-01-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多