如何计算一段时间内具有特定状态的用户数量?

时间:2012-11-28 19:00:48

标签: sql sql-server

我有一个报告,我需要从SQL Server 2008 R2中的UserHistory表创建。样本如下: report

出于计费目的,我们需要计算在特定时期内有多少不同的用户拥有特定状态。期限可以低至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个已取消的用户。

有没有人有任何想法或更好的方法去做这件事?我真的可以使用一些输入。

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