【问题标题】:SQL Server : Using LAG() with calculated previous valueSQL Server:使用 LAG() 计算先前的值
【发布时间】:2021-12-21 08:40:41
【问题描述】:

我在 SQL Server 2016 数据库表中有这样的数据:

PERIODE PERIODE_FORECAST VALUE
2021-08-01 2021-01-01 51384.673
2021-08-01 2021-02-01 44118.129
2021-08-01 2021-03-01 43164.446
2021-08-01 2021-04-01 38113.745
2021-08-01 2021-05-01 37306.956
2021-08-01 2021-06-01 38390.359
2021-08-01 2021-07-01 42692.390
2021-08-01 2021-08-01 39814.047
2021-08-01 2021-09-01 0.000
2021-08-01 2021-10-01 0.000
2021-08-01 2021-11-01 0.000
2021-08-01 2021-12-01 0.000

我有条件填写零值例如:

PERIODE PERIODE_FORECAST VALUE
2021-08-01 2021-09-01 39814.047 * 0.5
2021-08-01 2021-10-01 (39814.047 * 0.5 ) * 0.7
2021-08-01 2021-11-01 ((39814.047 * 0.5 ) * 0.7) * 0.5
2021-08-01 2021-12-01 (((39814.047 * 0.5 ) * 0.7) * 0.5) * n

我正在尝试使用LAG() 函数,但它没有如我所愿。

查询:

SELECT
    PERIODE,PERIODE_FORECAST,
    CASE 
        WHEN PERIODE_FORECAST > PERIODE 
            THEN LAG(VALUE, 1, 0) OVER (ORDER BY PERIODE_FORECAST ASC) * [some function/other column values]
        ELSE VALUE 
    END VALUE
FROM 
    MyTable

我期待结果:

PERIODE PERIODE_FORECAST VALUE
2021-08-01 2021-01-01 51384.673
2021-08-01 2021-02-01 44118.129
2021-08-01 2021-03-01 43164.446
2021-08-01 2021-04-01 38113.745
2021-08-01 2021-05-01 37306.956
2021-08-01 2021-06-01 38390.359
2021-08-01 2021-07-01 42692.390
2021-08-01 2021-08-01 39814.047
2021-08-01 2021-09-01 19907.0235
2021-08-01 2021-10-01 result on 2021-09-01
2021-08-01 2021-11-01 result on 2021-10-01
2021-08-01 2021-12-01 result on 2021-11-01

如何使用上一个计算的行,并在当前行的计算中使用它?

有没有不创建存储过程的解决方案或变通方法?

到目前为止,感谢您的回答。

【问题讨论】:

  • 您必须为此使用递归查询。 LAG 不起作用。
  • 如果按 PERIODE_FORECAST 排序,0 值是否总是在底部?
  • Phil Coulson,是的,0 总是在底部

标签: sql sql-server date datetime recursive-query


【解决方案1】:

您的问题是当您在第一个周期后 2 个月时滞后值 0。 我认为您必须为函数 lag() 的第二部分使用参数。 有点像: 滞后(VALUE, DATEDIFF(m,convert(date,'2021-08-01'),convert(date,'2021-10-01')) ,0 )

这里:

Lag(值, DATEDIFF(m,convert(date,PERIODE),convert(date,PERIODE_FORECAST)) ,0 )

您可能必须将此 datediff 添加为乘数 0.1 的指数。

【讨论】:

    【解决方案2】:

    这是一个有趣的问题,因为我是一名程序员,所以我的方法可能不是最高效的,但它确实产生了您期望的结果。我首先创建一个公用表表达式并使用 ROW_NUMBER 对行进行编号。然后我重新加入 CTE 以找到具有最高 ROW_NUMBER 的单个 VALUE。您可以使用 ROW_NUMBER 的差异作为 10.0000 的 POWER 来获得乘数。

    首先我创建了你的数据:

    DECLARE @Forecast AS TABLE
    (
        [PERIODE]          DATE           NOT NULL,
        [PERIODE_FORECAST] DATE           NOT NULL,
        [VALUE]            DECIMAL(16, 8) NOT NULL
    );
    
    INSERT INTO @Forecast
    (
        [PERIODE],
        [PERIODE_FORECAST],
        [VALUE]
    )
    VALUES
    ('2021-08-01', '2021-01-01', 51384.673),
    ('2021-08-01', '2021-02-01', 44118.129),
    ('2021-08-01', '2021-03-01', 43164.446),
    ('2021-08-01', '2021-04-01', 38113.745),
    ('2021-08-01', '2021-05-01', 37306.956),
    ('2021-08-01', '2021-06-01', 38390.359),
    ('2021-08-01', '2021-07-01', 42692.390),
    ('2021-08-01', '2021-08-01', 39814.047),
    ('2021-08-01', '2021-09-01', 0.000),
    ('2021-08-01', '2021-10-01', 0.000),
    ('2021-08-01', '2021-11-01', 0.000),
    ('2021-08-01', '2021-12-01', 0.000);
    

    一旦我有了这个,查询是:

    ;WITH [NumberedRows]
    AS (SELECT [PERIODE],
               [PERIODE_FORECAST],
               [VALUE],
               ROW_NUMBER() OVER (PARTITION BY [PERIODE]
                                  ORDER BY [PERIODE_FORECAST]
                                 ) AS [rn]
        FROM   @Forecast)
    SELECT [nr1].[PERIODE],
           [nr1].[PERIODE_FORECAST],
           CASE WHEN [sub].[VALUE] IS NOT NULL THEN
                    [sub].[VALUE] * POWER(10.0000000, ([sub].[rn] - [nr1].[rn]))
                ELSE
                    [nr1].[VALUE]
           END AS [VALUE]
    FROM   [NumberedRows] AS [nr1]
           LEFT OUTER JOIN
           (
               SELECT   TOP(1)
                        [nr2].[PERIODE], [nr2].[PERIODE_FORECAST], [nr2].[VALUE], [nr2].[rn]
               FROM     [NumberedRows] [nr2]
               WHERE    [nr2].[VALUE] <> 0
               ORDER BY [nr2].[rn] DESC
           ) AS [sub]
               ON [sub].[PERIODE] = [nr1].[PERIODE]
                  AND [sub].[rn] < [nr1].[rn];
    
    

    结果:

    PERIODE PERIODE_FORECAST VALUE
    2021-08-01 2021-01-01 51384.673000
    2021-08-01 2021-02-01 44118.129000
    2021-08-01 2021-03-01 43164.446000
    2021-08-01 2021-04-01 38113.745000
    2021-08-01 2021-05-01 37306.956000
    2021-08-01 2021-06-01 38390.359000
    2021-08-01 2021-07-01 42692.390000
    2021-08-01 2021-08-01 39814.047000
    2021-08-01 2021-09-01 3981.404700
    2021-08-01 2021-10-01 398.140470
    2021-08-01 2021-11-01 39.814047
    2021-08-01 2021-12-01 3.981405

    【讨论】:

    • 感谢您的回答,正如我在尝试您的查询后看到的那样,您的方法是创建/更新乘数。不解释是我的错。条件“THEN LAG(VALUE, 1, 0) OVER (ORDER BY PERIODE_FORECAST ASC) * 0.1”只是示例。 “0.1”可以是任何数字或函数。我编辑了这个问题。对不起我的英语不好
    【解决方案3】:

    这是间隙和孤岛问题,其中每个非零值都标志着一个新孤岛的开始。分组后,您可以使用first_value 选择相应的“先前值”,而row_number 用作计算乘数的偏移量。

    with A as (
        select *,
          sum(case when "value" <> 0 then 1 else 0 end)
            over (order by periode_forecast) as grp
        from T
    ), B as (
        select *,
          first_value("value")
            over (partition by grp order by periode_forecast) as pv,
          power(10e, 1 - row_number()
            over (partition by grp order by periode_forecast)) as mult
        from A
    )
    select periode_forecast, "value", grp, pv, pv * mult as new_value
    from B
    order by periode_forecast;
    

    您可能希望坚持使用 decimal 数学并避免使用 float。如果是这样,请调整 power() 引用中的 10e

    如果没有有效的先前非零行,则结果将为零。目前尚不清楚这是否会在您的数据中发生或如何区别对待。

    https://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=545d6e84ea43885788e45a2fb0393884

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-30
      • 2016-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-29
      • 2015-07-09
      • 1970-01-01
      相关资源
      最近更新 更多