简体   繁体   中英

Oracle sum most recent records until a defined value then Ignore the rest

Im looking to sum a column until a defined value then ignore the rest of the records.

ID WHEN VALUE AVG_COL
101 2016 6 84.5
101 2015 3 76
101 2014 3 87
101 2013 15 85.8
101 2012 6 92
101 2011 3 81
101 2010 3 82.3

I need a single result set of

ID VALUE AVG_COL
101 30 82.3

I have tried the following

SELECT
   ID,
   WHEN,
   VALUE,
   AVG_COL,
   SUM(VALUE) OVER (PARTITION BY ID ORDER BY WHEN) AS VALUE, --must equal 30
   AVG(AVG_COL) OVER (PARTITION BY ID) AVG
FROM
    TABLE_ONE
WHERE
   VALUE = 30;

Any help would be greatly appreciated!

Hi try some thing like this, where modified the WHERE clause

-- Untested

SELECT
   ID,
   WHEN,
   VALUE,
   AVG_COL,
   SUM(VALUE) OVER (PARTITION BY ID ORDER BY WHEN) AS VALUE, --must equal 30
   AVG(AVG_COL) OVER (PARTITION BY ID) AVG
FROM
    TABLE_ONE
WHERE
   ID IN (SELECT ID FROM ( (SELECT ID, sum(VALUE) sum_val FROM 
           TABLE_ONE GROUP BY ID) WHERE SUM_VAL = 30);

try this

select id,
SUM(VALUE) OVER (PARTITION BY ID ORDER BY WHEN RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS VALUE,
AVG(AVG_COL) OVER (PARTITION BY ID) AVG
from table_one
where VALUE <= 30
order by when desc
fetch first 1 rows only;

You can compute the running sum with window functions, then filter and limit in an outer query.

Assuming a table like (id, dt, val, . . .) :

select *
from (
    select t.*,
        sum(val) over(partition by id order by dt) sum_val
    from mytable t
) t
where sum_val >= 30
order by row_number() over(partition by id order by dt desc)
fetch first row with ties

Notes:

  • this handles multiple ids at once
  • for each id, this brings the first row where the running sum of the value reaches at least 30, if any (rows are processed by descending date)

What you need is running sum of values and a decision on what order by you want to apply for that running sum.
SAMPLE DATA

WITH
    tbl AS
        (
            Select 101 "ID", 2016 "YR",  6 "VAL", 84.5 "AVG_COL" From Dual Union All
            Select 101 "ID", 2015 "YR",  3 "VAL",   76 "AVG_COL" From Dual Union All
            Select 101 "ID", 2014 "YR",  3 "VAL",   87 "AVG_COL" From Dual Union All
            Select 101 "ID", 2013 "YR", 15 "VAL", 85.8 "AVG_COL" From Dual Union All
            Select 101 "ID", 2012 "YR",  6 "VAL",   92 "AVG_COL" From Dual Union All
            Select 101 "ID", 2011 "YR",  3 "VAL",   81 "AVG_COL" From Dual Union All
            Select 101 "ID", 2010 "YR",  3 "VAL", 82.3 "AVG_COL" From Dual 
        ),

Create CTE (I named it grid) to prepare your data - in this case I used descending order by years:

  grid AS
    (
        Select
          ID, YR, 
          VAL, 
          Sum(VAL) OVER(Partition By ID Order By YR DESC Rows Between Unbounded Preceding And Current Row) "RUNNING_SUM",
          CASE WHEN Sum(VAL) OVER(Partition By ID Order By YR DESC Rows Between Unbounded Preceding And Current Row) >= 30
               THEN Sum(1) OVER(Partition By ID Order By YR DESC Rows Between Unbounded Preceding And Current Row)  
          END "IS_OVER_RN",
          AVG_COL,
          Round(AVG(AVG_COL) OVER(Partition By ID Order By YR DESC Rows Between Unbounded Preceding And Current Row), 2) "RUNNING_AVG"
        From
          tbl
    )

This cte is resulting as:

        ID         YR        VAL RUNNING_SUM IS_OVER_RN    AVG_COL RUNNING_AVG
---------- ---------- ---------- ----------- ---------- ---------- -----------
       101       2016          6           6                  84.5        84.5 
       101       2015          3           9                    76       80.25 
       101       2014          3          12                    87        82.5 
       101       2013         15          27                  85.8       83.33 
       101       2012          6          33          5         92       85.06 
       101       2011          3          36          6         81       84.38 
       101       2010          3          39          7       82.3       84.09

Now you can get your result with the code below

-- Main SQL 
SELECT
    ID, RUNNING_SUM, RUNNING_AVG
FROM
    grid g
WHERE IS_OVER_RN = (Select Min(IS_OVER_RN) From grid Where ID = g.ID)

Result:

-- with YEARS in DESCENDING order
        ID RUNNING_SUM RUNNING_AVG
---------- ----------- -----------
       101          33       85.06 

If you make orderings within analytic functions in grid cte ASCENDING then the same main SQL would result with:

-- with YEARS in ASCENDING order
        ID RUNNING_SUM RUNNING_AVG
---------- ----------- -----------
       101          30        82.5 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM