2011-12-26 2 views
3

Учитывая таблицу полученных очков пользователей, мне нужно рассчитать полугодовой период полураспада накопленных очков. То есть, все баллы, полученные в течение последних 6 месяцев оценивается на уровне 100%, 6-12 месяцев на 50%, 12-18 месяцев 25% и т.д.T-SQL Row by Row Decay

Вот моя лучшая попытка с помощью КТР:

DECLARE @t AS TABLE (
    ID int NOT NULL, 
    DT SMALLDATETIME NOT NULL, 
    POINTS int NOT NULL, 
    UserId INT NOT null, 
    POINTS_TOTAL INT NOT NULL) 
INSERT INTO @t 
VALUES (1, '1/1/2010', 20, 1, 20) 
    ,(5, '7/1/2010', 30, 1, 50) 
    ,(7, '1/1/2011', 10, 1, 60) 
    ,(8, '7/1/2011', 15, 1, 75) 
    ,(9, '12/25/2011', 15, 1, 90) 

;WITH ctePts AS 
    (SELECT t.*, POINTS AS Decay 
    FROM @t AS t 
    WHERE t.DT > DATEADD(mm, 6, GETUTCDATE()) 

    UNION ALL 

    SELECT t.ID, t.DT, t.POINTS, t.UserId, t.POINTS_TOTAL, CAST(ctePts.Decay * 0.5 + t.POINTS AS INT) AS Decay 
    FROM ctePts 
    INNER JOIN @t AS t 
     ON t.UserId = ctePts.UserId 
      AND t.DT <= ctePts.DT AND t.DT > DATEADD(mm, 6, ctePts.DT) 
) 
SELECT * 
FROM ctePts 

В итоговой таблице будет столбец HALF_LIFE, который содержит скорректированную сумму всех очков, полученных для этого пользователя. Я должен быть в состоянии выполнить следующий запрос, чтобы получить статус текущей точки пользователя (всего набранные очки эти с поправкой на период полураспада распада) для любой данный момент времени:

SELECT UserIdInt, POINTS, POINTS_HALFLIFE FROM 
    (SELECT UserIdInt, 
       ISNULL(p.POINTS_TOTAL,0) AS POINTS, ISNULL(p.POINTS_HALFLIFE,0) POINTS_HALFLIFE, 
       ROW_NUMBER() OVER (Partition BY UserIdInt ORDER BY p.ID DESC) AS rownum 

    FROM dbo.USER_POINTS p) a 
WHERE rownum = 1 

ответ

2

Является ли это то, что вам нужно?

SELECT *, 
     POINTS/POWER(2E0, ((DATEDIFF(MONTH, DT, GETUTCDATE()) - 1 + 
          CASE 
          WHEN DAY(DT) <= DAY(GETUTCDATE()) THEN 1 
          ELSE 0 
          END)/6)) 
      AS POINTS_HALFLIFE 
FROM @t 

Возвращает

ID   DT      POINTS  UserId  POINTS_TOTAL POINTS_HALFLIFE 
----------- ----------------------- ----------- ----------- ------------ ---------------------- 
1   2010-01-01 00:00:00  20   1   20   2.5 
5   2010-07-01 00:00:00  30   1   50   7.5 
7   2011-01-01 00:00:00  10   1   60   5 
8   2011-07-01 00:00:00  15   1   75   15 
9   2011-12-25 00:00:00  15   1   90   15 
+0

Если это то, что он ищет, это пятно. +1 для использования POWER. – Paparazzi

+0

Согласовано. Мой вопрос мог быть более ясным. Будет редактировать для потомства. Хотя это не то, что я намеревался, этот ответ доставит меня туда. Я второй БаламБалам. Очень гладкий оптимизированный подход. +1. – Laramie

0

Я не до конца понимаю вопрос, но я думаю, что вы ищете по-пользователя так группе идентификаторов пользователей. Если вам не нужно группировать по идентификатору пользователя, просто удалите его в группе select и group by.

select base.UserId, SUM([full].[POINTS]) as [fullPoints], SUM([half].[POINTS])/2 as [halfPoints] 
    , SUM([quarter].[POINTS])/4 as [quarterPoints] 
    , (SUM([full].[POINTS]) + SUM([half].[POINTS])/2 + SUM([quarter].[POINTS])/4) as [totalPoints] 
    from @t as [base] 
    left outer join @t as [full] 
     on base.ID = [full].ID 
     and DATEDIFF(MM, [full].DT, GETDATE()) <= 6 
    left outer join @t as [half] 
     on base.ID = [half].ID 
     and DATEDIFF(MM, [half].DT, GETDATE()) > 6 
     and DATEDIFF(MM, [half].DT, GETDATE()) <= 12 
    left outer join @t as [quarter] 
     on base.ID = [quarter].ID 
     and DATEDIFF(MM, [quarter].DT, GETDATE()) > 12 
    group by base.UserId