【问题标题】:Calculate periodic values from year to date figures in SQL在 SQL 中计算从年份到日期的周期值
【发布时间】:2018-09-17 08:36:58
【问题描述】:

我已经积累了每个时期的年初至今值。 但我需要每个产品组/周期的周期性值。

您知道 SQL Server 中的解决方案可以以简单的方式实现这一目标吗?

这是我的示例数据:

Product Group | Period | Amount
-------------------------------
Group 1       |2018/01 | 500
Group 1       |2018/02 | 740
Group 1       |2018/03 | 900
Group 1       |2018/04 | 930

结果应该是这样的:

Product Group | Period | Amount
-------------------------------
Group 1       |2018/01 | 500
Group 1       |2018/02 | 240
Group 1       |2018/03 | 160
Group 1       |2018/04 | 30

感谢您的帮助! 菲利普

【问题讨论】:

  • 大多数 SQL 都有窗口函数,我不确定 SQL Server 是否有。在文档中搜索 LAG。

标签: sql sql-server calculation accumulate periodicity


【解决方案1】:

假设您不能使用 LAG(例如 MSSQL 2008)并且您可以为每个 ProductGroup 和 Period 拥有多个记录,您可以使用以下查询。

示例表和值:

CREATE TABLE GR (Product_Group VARCHAR(10), Period VARCHAR(6), Amount INT);

INSERT INTO GR VALUES ('Group 1',  '201801', 500)
, ('Group 1',  '201802', 740)
,('Group 1',  '201803', 900)
,('Group 1',  '201804', 930)
;
INSERT INTO GR VALUES ('Group 2',  '201801', 500)
, ('Group 2',  '201803', 800)
,('Group 2',  '201803', 1000)
,('Group 2',  '201804', 1200)
;

查询使用 GROUP BY(和 CTE 来简化阅读)对 ProductGroup 和 Period 进行分组,并使用 RowNumber 来查找先前的值(如果您有固定的期间,即如果您想在缺少月份值时显示每个月的记录您可以使用 Tally Date 表)

WITH X AS (SELECT  Product_Group, Period, SUM(Amount) AS Amount_TOT
            , ROW_NUMBER() OVER (PARTITION BY Product_Group ORDER BY PERIOD) AS RN
            FROM GR GROUP BY Product_group, Period)  
SELECT Product_Group, Period, Amount_TOT,  Amount_TOT_PREC, Amount_TOT-ISNULL(Amount_TOT_PREC,0) AS Delta 
FROM (SELECT  A.Product_Group, A.Period, A.Amount_TOT, B.Amount_TOT AS Amount_TOT_PREC       
        FROM X A
        LEFT JOIN X B ON A.Product_Group=B.Product_Group AND A.RN-1 = B.RN
        ) C 

输出

+---------------+--------+------------+-----------------+-------+
| Product_Group | Period | Amount_TOT | Amount_TOT_PREC | Delta |
+---------------+--------+------------+-----------------+-------+
| Group 1       | 201801 |        500 | NULL            |   500 |
| Group 1       | 201802 |        740 | 500             |   240 |
| Group 1       | 201803 |        900 | 740             |   160 |
| Group 1       | 201804 |        930 | 900             |    30 |
| Group 2       | 201801 |        500 | NULL            |   500 |
| Group 2       | 201803 |       1800 | 500             |  1300 |
| Group 2       | 201804 |       1200 | 1800            |  -600 |
+---------------+--------+------------+-----------------+-------+

使用 LAG(此函数返回上一条记录的值,请查看 Microsoft 文档)更快且更具可读性:

WITH X AS (SELECT Product_Group, Period
                  , SUM(Amount) AS Amount_TOT           
            FROM GR GROUP BY Product_group, Period)  
SELECT Product_Group, Period, Amount_TOT, AMOUNT_PREC
       , Amount_TOT-ISNULL(AMOUNT_PREC,0) AS Delta 
FROM (SELECT  Product_Group, Period, Amount_TOT 
              , LAG(Amount_TOT) OVER (PARTITION BY Product_Group 
                 ORDER BY PERIOD) AS AMOUNT_PREC     
        FROM X) A;

与上面的输出相同

【讨论】:

    【解决方案2】:

    你可以像这样使用LAG函数:

    SELECT [Product Group], Period,  
           Amount - LAG(Amount, 1,0) OVER (ORDER BY Period) AS Amount
    FROM myTable 
    

    【讨论】:

      【解决方案3】:

      希望这会有所帮助:

      Select 
      ProductGroup, 
      Period, 
      Amount = Amount-ISNULL((Select Amount from TempTable where Period = (select max(Period) from TempTable where Period < Main.Period)),0)
      From TempTable Main
      Order by Period
      

      我假设 Period 是日期时间类型 (2018-01-01)

      【讨论】:

        【解决方案4】:

        希望对你有帮助。

        WITH CTE AS (
        SELECT
        rownum = ROW_NUMBER() OVER (ORDER BY t.[Product Group],t.[Period]),
        t.*
        FROM YourTbl AS t
        )
        
        SELECT CTE.[Product Group],CTE.Period,-1*(ISNULL(prev.Amount,0)-CTE.Amount) AS Amount 
        FROM CTE
        LEFT JOIN CTE prev ON prev.rownum = CTE.rownum - 1
        

        【讨论】:

          【解决方案5】:

          我会使用lag() 函数:

          select [Product Group], Period,  
                 Amount - LAG(Amount, 1,0) OVER (ORDER BY Period) AS Amount
          from table t'
          

          如果你有旧版本的SQL,那么你也可以使用apply

          select t.product, t.period, coalesce(t.amount-t1.amount, t.amount) as amount
          from table t outer apply
               ( select top (1) t1.*
                 from table t1
                 where t1.product = t.product and 
                       t1.period < t.period
                       order by t1.period desc
               ) t1;
          

          【讨论】:

            【解决方案6】:

            查询中的一个小变化

            SELECT [ProductGroup], Period,  
                  Amount- LAG(Amount, 1,0) OVER (ORDER BY Period)   AS Amount
            FROM tablename
            

            【讨论】:

              猜你喜欢
              • 2020-07-10
              • 2010-11-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-12-14
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多