某个日期范围内每个日期在给定日期打开的订单总数

mg1*_*075 5 sql-server-2008 t-sql

这个问题类似于Running total with count? ,但请允许我进一步解释这个问题。

我使用的是 SQL Server 2008,因此 Aaron Bertrand 描述的游标选项在速度方面似乎是最有前途的。

不过,这里的不同之处在于我必须考虑单个项目的两个日期。因此,给定订单表中的 OrderID 项,OrderID 具有 Opened Date 和 Closed Date。Opened Date 总是填充的,但 Closed Date 可以是NULL

OrderID  OpenedDate    ClosedDate
654554    12/1/2011     5/4/2012
678451    12/4/2011     3/2/2012
679565    12/8/2011     5/21/2012
701541    5/23/2012     NULL
...
Run Code Online (Sandbox Code Playgroud)

我需要 - 有效地 - 获取在日期范围内的任何给定日期有多少订单具有我们可以称为“打开”状态的状态。日期范围可能跨越几年。(是的,我确实有一个连续日期的参考表。)

Date         CountOfOpenOrders
12/1/2011    175
12/2/2011    178
12/3/2011    195
12/4/2011    192
12/5/2011    191
...
Run Code Online (Sandbox Code Playgroud)

A-K*_*A-K 4

如果您优先考虑的是选择速度,则以下方法可以实现极快的选择。您想要存储两个事件(周期开始和周期结束),而不是存储周期。当时间段开始时,更改列为 1,当时间段结束时,更改列为 -1。如果同一天发生多个事件,则它们必须具有不同的 EventNumberPerDay。RunningTotal 是事件发生后开放周期的数量:

CREATE TABLE dbo.Events(
 PeriodId INT NOT NULL,
 Change SMALLINT NOT NULL,
 ChangeDate DATE NOT NULL,
 EventNumberPerDay INT NOT NULL,
 RunningTotal INT NOT NULL);
GO

INSERT dbo.Events
        ( PeriodId ,
          Change ,
          ChangeDate ,
          EventNumberPerDay,
          RunningTotal
        )
-- first period begins    
VALUES  ( 1 , 1, '20120801', 1, 1),
-- second period begins on the same day
(2, 1, '20120801', 2, 2),
-- third period begins
(3,1,'20120803',1, 3),
-- second period ends on the same day
(2,-1,'20120803',2, 2),
-- fourth period begins
(4,1,'20120804',1,3),
-- fourth period ends
(4,-1,'20120805',1,2),
-- first period ends
(1, -1, '20120808',1,  1),
-- third period ends
(3, -1, '20120809',1,  0);
GO
Run Code Online (Sandbox Code Playgroud)

您还需要一个日历表:

CREATE TABLE dbo.Calendar([Date] DATE NOT NULL);
GO

INSERT INTO dbo.Calendar([Date])
VALUES('20120801'),
('20120802'),
('20120803'),
('20120804'),
('20120805'),
('20120806'),
('20120807'),
('20120808'),
('20120809'),
('20120810'),
('20120811');
Run Code Online (Sandbox Code Playgroud)

完成后,您的选择将非常简单且快速:

SELECT  [Date] ,
        coalesce(RunningTotal, 0) AS NumOpenIntervals
FROM    dbo.Calendar
        OUTER APPLY ( SELECT TOP ( 1 )
                                RunningTotal
                      FROM      dbo.Events
                      WHERE     ChangeDate <= [Date]
                      ORDER BY  ChangeDate DESC, EventNumberPerDay DESC
                    ) AS t
ORDER BY [Date]
Run Code Online (Sandbox Code Playgroud)

当然,只有当Events表中的数据有效时,这个查询才是正确的。我们可以使用约束来确保 100% 的数据完整性。如果你有兴趣的话我可以解释一下。

另一种选择是将原始数据(即经期)加载到客户端应用程序中 - 在 C++/C#/Java 中,您的问题绝对微不足道。

另一种方法是使用具有快速游标的 RDBMS,例如 Oracle - 这将允许您只编写一个简单的游标并享受良好的性能,但仍然不如我的第一个解决方案。