1

我有一个需要从 SQL Server 2008 R2 中的 UserHistory 表创建的报告。示例如下: 报告

出于计费目的,我们需要计算在特定时期内有多少不同用户具有特定状态。这段时间可以低至 1 天,也可以长达 2 个月。因此,如果一个用户在一段时间内处于活动状态、不活动状态然后再次处于活动状态,那么这将被视为一个活动用户。

曲线球现在可以上场了。如果我在指定时间段内没有历史记录,则存储过程需要在该时间段开始之前寻找第一个历史记录条目,以查看它是否与状态匹配。显然,如果我在 10 月 20 日活跃,然后我的帐户在 11 月 15 日被暂停,如果这段时间是 11 月,我仍然活跃。

起初,我用一个游标遍历每个用户表来执行此操作,并对每个用户及其用户历史进行查询,但是即使使用相关索引,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 名用户被取消。

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

4

1 回答 1

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
于 2012-11-29T09:34:52.050 回答