【问题标题】:T-SQL Row by Row DecayT-SQL 逐行衰减
【发布时间】:2012-01-28 03:25:05
【问题描述】:

给定一个获得的用户积分表,我需要计算获得的累积积分的六个月半衰期。也就是说,在过去 6 个月内获得的所有积分都以 100% 的价值计算,6-12 个月的价值为 50%,12-18 个月的 %25 等。

这是我使用 CTE 的最佳尝试:

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

【问题讨论】:

    标签: sql-server-2008 tsql user-defined-functions


    【解决方案1】:

    我不完全理解这个问题,但我认为您正在查看用户 ID,因此按用户 ID 分组。如果您不需要按用户 ID 分组,则只需在选择和分组依据中将其删除。

        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
    

    【讨论】:

      【解决方案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
      

      【讨论】:

      • 如果那是他正在寻找的东西,那就太好了。 +1 使用 POWER。
      • 同意。我的问题本来可以更清楚。将为后代编辑。虽然不是我想要的,但这个答案会让我到达那里。我第二个BalamBalam。非常巧妙的优化方法。 +1。
      猜你喜欢
      • 2019-05-31
      • 2016-04-15
      • 2020-08-09
      • 1970-01-01
      • 2011-11-02
      • 2018-03-22
      • 2015-06-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多