简体   繁体   中英

SQL: How to select a column-value based on an aggregate min/max value in a window frame (including preceding rows)

I have the following table:

| Date     | Value   | Name | AnticipatedValue |
| -------- | ------- | ---- | ---------------- |
| 27.11.20 | 639.600 | col1 |                  |
| 30.11.20 | 638.300 | col2 |                  |
| 01.12.20 | 638.000 | col3 | col1             |
| 02.12.20 | 642.600 | col4 | col1             |
| 03.12.20 | 646.200 | col5 | col1             |
| 04.12.20 | 651.900 | col6 | col4             |
| 07.12.20 | 651.800 | col7 | col4             |
| 08.12.20 | 643.800 | col8 | col6             |
| 09.12.20 | 654.250 | col9 | col6             |

I want the name from the row that has the max value between the 2nd and 5th preceding row. The column AnticipatedValue shows my desired result.

I am currently using a window function to get the max value in that example, however I am missing a way to get the corresponding name of that max value . My current code looks like this:

MAX(value) OVER (ORDER BY date ROWS BETWEEN 5 PRECEDING AND 2 PRECEDING)

I think what would help me most would be if I were able to do another ORDER BY inside of the window frame itself. Then I could just use an order by descending by value and take the first name that I get. However this isn't possible / implement yet in aggregate functions in sql.

Also using a subquery to get the corresponding name is quite difficult imo, as I would still have to apply the window frame (ie preceding 2nd and 5th row) inside of the subquery.

I am using Postgres 12.6.

I would highly appreciate any help regarding this sql riddle. I feel that I am not far from the solution, but couldn't find any elegant way to do it.

Update I took Gordon Linoff's solution and adjusted it by using a left join and adding a limit 1 to get the above table that I desired:

select t.*, t2.*
from t left join lateral
     (select t2.name, t2.value
      from (select t2.name, t2.value
            from t t2
            where t2.date < t.date
            order by t2.date desc
            offset 1 fetch first 4 rows only
           ) t2
       order by value desc
      limit 1
      ) t2 ON true;

You can use a lateral join:

select t.*, t2.*
from t cross join lateral
     (select t2.*
      from (select t2.name, t2.value
            from t t2
            where t2.date < t.date
            order by t2.date desc
            offset 1 fetch first 4 rows only
           ) t2
       order by value desc
      ) t2;

  

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