1

SQL Server 2008 R2 の UserHistory テーブルからレポートを作成する必要があります。以下にサンプルを示します。 報告

請求の目的で、特定の期間にわたって特定のステータスを持っていた個別のユーザーの数を計算する必要があります。期間は最短で 1 日、最長で 2 か月程度です。したがって、1 人のユーザーがアクティブで、アクティブではなく、その後再びアクティブになった場合、それは 1 人のアクティブ ユーザーとしてカウントされます。

カーブボールが登場します。指定された期間内に履歴がなかった場合、ストアド プロシージャは期間の開始前に最初の履歴エントリを探して、それがステータスと一致するかどうかを確認する必要があります。明らかに、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 人いたことは合計に反映されていません。

これを行うためのアイデアやより良い方法はありますか? 私は本当にいくつかの入力を使用できます。

4

1 に答える 1

0

範囲全体に対してストアド プロシージャを 1 回呼び出すことにしました。これにより、条件に一致するすべての UserHistory エントリが返され、コード内で必要に応じてカウントおよびフィルタリングできます。

DB からアプリケーションに大量のデータが転送される可能性があるため、理想的ではありません。また、アプリに入ると少しメモリを使用する可能性がありますが、動作し、高速で、さらに重要なことに正確です。

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 に答える