我有一个报告,我需要从SQL Server 2008 R2中的UserHistory表创建。样本如下:
出于计费目的,我们需要计算在特定时期内有多少不同的用户拥有特定状态。期限可以低至1天或高达2个月。因此,如果一个用户处于活动状态,未处于活动状态,然后在一段时间内再次处于活动状
曲线球现在进场了。如果我在指定的时间段内没有历史记录,则存储过程需要在句点开始之前搜索第一个历史记录条目,以查看它是否与状态匹配。很明显,如果我在10月20日活跃,然后我的帐户在11月15日暂停,如果期间是11月,我仍然活跃。
首先,我使用光标遍历每个User表,并按用户执行查询及其用户历史记录,但即使使用相关索引,对于200000个用户也需要大约40秒。在9个状态的一段时间内每天40秒加起来非常快。
我的同事和我想出了最接近的方法:
ALTER PROCEDURE [dbo].[cpUserHistory_CountByStatus] @StartDate DATETIME2,
@EndDate DATETIME2,
@Status VARCHAR(50)
AS
SET NOCOUNT ON
SELECT SUM(CASE WHEN CurrentMonth.UserId IS NOT NULL OR PreviousTime.UserId IS NOT NULL THEN 1 ELSE 0 END)
FROM [User]
LEFT JOIN
(SELECT UserId
FROM
UserHistory
INNER JOIN
(SELECT MAX(UserHistoryId) AS UserHistoryId
FROM UserHistory
WHERE EditedDate BETWEEN @StartDate AND @EndDate
GROUP BY UserId) LastStatus ON UserHistory.UserHistoryId = LastStatus.UserHistoryId
WHERE UserHistory.Status = @Status -- EDIT: Your status you're interested in
) CurrentMonth ON [User].UserId = CurrentMonth.UserId
LEFT JOIN
(SELECT UserId
FROM
UserHistory
INNER JOIN
(SELECT MAX(UserHistoryId) AS UserHistoryId
FROM UserHistory
WHERE EditedDate < @StartDate
GROUP BY UserId) LastStatus ON UserHistory.UserHistoryId = LastStatus.UserHistoryId
WHERE UserHistory.Status = @Status -- EDIT: Your status you're interested in
) PreviousTime ON [User].UserId = PreviousTime.UserId
但正如您在简化报告示例中所看到的,用户在当月3日被取消,然后在5日重新激活,但是总数并未反映在该期间的某个时刻有1个已取消的用户。
有没有人有任何想法或更好的方法去做这件事?我真的可以使用一些输入。
答案 0 :(得分:0)
我决定为整个范围调用一次存储过程,这将返回符合我的条件的所有UserHistory条目,并且在代码中我可以根据我的需要对其进行计数和过滤。
不太理想,因为可能有很多数据从数据库传输到应用程序,并且一旦在应用程序中它可以使用一点内存 - 但它可以工作,速度快,更重要的是准确。
ALTER PROCEDURE [dbo].[cpUserHistory_SelectForBillingPeriod] @StartDate DATETIME2,
@EndDate DATETIME2
AS
BEGIN
DECLARE @Uh TABLE (
UserHistoryId INT,
UserId INT,
UserStatus VARCHAR(50),
EditedDate DATETIME2
)
INSERT INTO @Uh
SELECT UserHistoryId,
UserId,
[Status],
EditedDate
FROM UserHistory
WHERE EditedDate >= @StartDate
AND EditedDate <= @EndDate
INSERT INTO @Uh
SELECT UserHistory.UserHistoryId,
UserId,
[Status],
EditedDate
FROM UserHistory
INNER JOIN (
SELECT MAX(UserHistoryId) AS UserHistoryId
FROM UserHistory
WHERE EditedDate < @StartDate
GROUP BY UserId
) LastStatus ON UserHistory.UserHistoryId = LastStatus.UserHistoryId
SELECT *
FROM @Uh
order by EditedDate desc