从每个组中检索列的最后一个元素,并将其用作下一组中同一列的第一个元素

时间:2019-01-10 13:06:05

标签: sql sql-server

我有一张桌子,如下表

Domain     Baseline     PlanningPeriod     Actual   
-------------------------------------------------      
Dom 1       Jan 18        1/1/18            10
Dom 1       Jan 18        1/2/18            20
Dom 1       Jan 18        1/3/18            25
Dom 1       Jan 18        1/4/18            24
Dom 1       Feb 18        1/2/18            60
Dom 1       Feb 18        1/3/18            20
Dom 1       Feb 18        1/5/18            40
Dom 1       Feb 18        1/12/18           70
Dom 1       Mar 18        1/3/18            11
Dom 1       Mar 18        1/4/18            12
Dom 1       Mar 18        1/12/18           18
Dom 1       Apr 18        1/4/18            40
Dom 1       Apr 18        1/5/18            25
Dom 1       Apr 18        1/6/18            15
-------------------------------------------------

请注意,域也不同

因此,我试图按域,基线,计划时间分组来获得额外的列交换性实际值。

为此,我做了以下查询

select domain,baseline,planningperiod, actual,sum(actual) over(partition 
by domain, baseline order by domain,baseline,planningperiod) as cumilativeactual

幸运的是,它按预期工作,并给出了以下结果

Domain     Baseline     PlanningPeriod     Actual    CumActual 
----------------------------------------------------------------      
Dom 1       Jan 18        1/1/18            10        10
Dom 1       Jan 18        1/2/18            20        30
Dom 1       Jan 18        1/3/18            25        55
Dom 1       Jan 18        1/4/18            24        79
Dom 1       Feb 18        1/2/18            60        60
Dom 1       Feb 18        1/3/18            20        80
Dom 1       Feb 18        1/5/18            40        120
Dom 1       Feb 18        1/12/18           70        190
Dom 1       Mar 18        1/3/18            11        11
Dom 1       Mar 18        1/4/18            12        23
Dom 1       Mar 18        1/12/18           18        31
Dom 1       Apr 18        1/4/18            40        40
Dom 1       Apr 18        1/5/18            25        65
Dom 1       Apr 18        1/6/18            15        80
--------------------------------------------------------

但是我的要求有点疯狂。我需要每个组的最后一个元素作为下一组的第一个元素。但是组中的其他值与上面完全相同。所以我真的很困惑如何实现这一目标。我不喜欢使用游标,因为大约有2万条记录。

以上方法应如下工作。

Domain     Baseline     PlanningPeriod     Actual    CumActual 
----------------------------------------------------------------      
Dom 1       Jan 18        1/1/18            10        10
Dom 1       Jan 18        1/2/18            20        30
Dom 1       Jan 18        1/3/18            25        55
Dom 1       Jan 18        1/4/18            24        79
Dom 1       Feb 18        1/2/18            60        **79**
Dom 1       Feb 18        1/3/18            20        80
Dom 1       Feb 18        1/5/18            40        120
Dom 1       Feb 18        1/12/18           70        190
Dom 1       Mar 18        1/3/18            11        **190**
Dom 1       Mar 18        1/4/18            12        23
Dom 1       Mar 18        1/12/18           18        31
Dom 1       Apr 18        1/4/18            40        **31**
Dom 1       Apr 18        1/5/18            25        65
Dom 1       Apr 18        1/6/18            15        80
--------------------------------------------------------

5 个答案:

答案 0 :(得分:5)

您可以将CTE和LAG()用作

with cte as
(
select domain,
       baseline,
       planningperiod, 
       actual,
       sum(actual) over(partition 
       by domain, baseline order by domain,baseline,planningperiod) as cumilativeactual
from t
)
select domain,
       baseline,
       planningperiod, 
       actual,
       case when actual = cumilativeactual then
            lag(cumilativeactual, 1, 0) over(order by domain,baseline,planningperiod)
            else
            cumilativeactual end as cumilativeactual
from cte

Demo

答案 1 :(得分:2)

您似乎希望使用一些其他逻辑来求和:

select domain, baseline, planningperiod, actual,
       (case when seqnum = 1
             then lag(periodtotal) over (partition by domain, baseline order by domain, baseline, planningperiod)
             else cumulativeactual
        end) as cum_actual
from (select domain, baseline, planningperiod, actual,
             sum(actual) over (partition by domain, baseline order by domain, baseline, planningperiod) as cumulativeactual,
             sum(actual) over (partition by domain, baseline) as periodtotal,
             row_number() over (partition by domain, baseline order by domain, baseline, planningperiod) a seqnum
      from t
     ) t;

答案 2 :(得分:2)

对于每个组,一些窗口功能可以帮助您将原始滚动总和替换为第一滚动总和。

示例数据(与示例相同的组和值):

IF OBJECT_ID('tempdb..#Amounts') IS NOT NULL
    DROP TABLE #Amounts

CREATE TABLE #Amounts (
    Code VARCHAR(10),
    Actual INT,
    Ordering INT IDENTITY)

INSERT INTO #Amounts (
    Code,
    Actual)
VALUES
    ('A', 10),
    ('A', 20),
    ('A', 25),
    ('A', 24),

    ('B', 60),
    ('B', 20),
    ('B', 40),
    ('B', 70),

    ('C', 11),
    ('C', 12),
    ('C', 18),

    ('D', 40),
    ('D', 25),
    ('D', 15)

查询

;WITH WindowFunctions AS
(
    SELECT
        A.Code,
        A.Actual,
        DenseRank = DENSE_RANK() OVER (ORDER BY A.Code),
        RowNumberByCodeDesc = ROW_NUMBER() OVER (PARTITION BY A.Code ORDER BY A.Ordering DESC),
        RowNumberByCodeAsc = ROW_NUMBER() OVER (PARTITION BY A.Code ORDER BY A.Ordering ASC),
        CumulativeGeneral = SUM(A.Actual) OVER (PARTITION BY A.Code ORDER BY A.Ordering ASC)
    FROM
        #Amounts AS A
)
SELECT
    G.Code,
    G.Actual,
    G.DenseRank,
    G.RowNumberByCodeAsc,
    G.RowNumberByCodeDesc,
    G.CumulativeGeneral,
    PreviousFinishingCumulativeGeneral = P.CumulativeGeneral,
    CorrectedCumulative = CASE 
        WHEN G.RowNumberByCodeAsc = 1 AND P.CumulativeGeneral IS NOT NULL THEN P.CumulativeGeneral
        ELSE G.CumulativeGeneral END
FROM
    WindowFunctions AS G
    LEFT JOIN WindowFunctions AS P ON
        G.DenseRank - 1 = P.DenseRank AND
        P.RowNumberByCodeDesc = 1

结果

Code    Actual  DenseRank   RowNumberByCodeAsc  RowNumberByCodeDesc CumulativeGeneral   PreviousFinishingCumulativeGeneral  CorrectedCumulative
A       10      1           1                   4                   10                  NULL                                10
A       20      1           2                   3                   30                  NULL                                30
A       25      1           3                   2                   55                  NULL                                55
A       24      1           4                   1                   79                  NULL                                79
B       60      2           1                   4                   60                  79                                  79
B       20      2           2                   3                   80                  79                                  80
B       40      2           3                   2                   120                 79                                  120
B       70      2           4                   1                   190                 79                                  190
C       11      3           1                   3                   11                  190                                 190
C       12      3           2                   2                   23                  190                                 23
C       18      3           3                   1                   41                  190                                 41
D       40      4           1                   3                   40                  41                                  41
D       25      4           2                   2                   65                  41                                  65
D       15      4           3                   1                   80                  41                                  80

答案 3 :(得分:0)

我猜因为输出是按cumilativeactual排序的,因此您可以只使用case when first_value/min_value of group then (select max(CumActual) from prev_group (using row_number()-1)

答案 4 :(得分:0)

感谢@Sami提供了不错的解决方案。但是我对此进行了一些调整,我想它可能会使它更加准确。

我的查询是

with cte as
(
select domain,
       baseline,
       planningperiod, 
       actual,
       sum(actual) over(partition 
       by domain, baseline order by domain,baseline,planningperiod) as cumilativeactual,DENSE_RANK() over(order by domain,baseline) as DRank 
from t
)
select domain,
       baseline,
       planningperiod, 
       actual,
       case when Drank> LAG(DRank,1,0) over(order by domain,baseline,planningperiod) then
            lag(cumilativeactual, 1, 0) over(order by domain,baseline,planningperiod)
            else
            cumilativeactual end as cumilativeactual
from cte