janos
janos

Reputation: 124656

SQL query: select the last record where a value went below a threshold

Given this example data set:

-----------------------------
| item   | date       | val |
-----------------------------
| apple  | 2012-01-11 |  15 |
| apple  | 2012-02-12 |  19 |
| apple  | 2012-03-13 |   7 |
| apple  | 2012-04-14 |   6 |
| orange | 2012-01-11 |  15 |
| orange | 2012-02-12 |   8 |
| orange | 2012-03-13 |  11 |
| orange | 2012-04-14 |   9 |
| peach  | 2012-03-13 |   5 |
| peach  | 2012-04-14 |  15 |
-----------------------------

I'm looking for the query that for each item, it will select the first date where the val went below CONST=10 without coming back above afterwards. In this example that would be:

-----------------------------
| item   | date       | val |
-----------------------------
| apple  | 2012-03-13 |   7 |
| orange | 2012-04-14 |   9 |
-----------------------------

Is this even possible without using cursors? I'm looking for this in Sybase.

If this is not possible without cursors, I can process the records in a programming language. In that case however, since in my real use case the full history is very long, I need a "suitable" query that selects just "enough" records for computing the record I am ultimately after: the most recent record where val dipped below CONST without coming back above it.

Upvotes: 2

Views: 1240

Answers (2)

t-clausen.dk
t-clausen.dk

Reputation: 44326

For MySql:

select t.item, t.date1, t.val
from 
(
  select min(date) date1, item from tablename  t
  where not exists (select 1 from tablename where item = t.item 
                    and date1 > t.date1 and val >= 10)
and val < 10
  group by item
) a
inner join 
@t t
on a.item = t.item and a.date1 = t.date1

For different databases such as MS-sql 2005+:

select item, date, val from 
(
  select *, row_number() over (partition by item order by date) rn
  from tablename t
  where not exists (select 1 from @t where item = t.item and 
                    date > t.date and val >= 10)
  and val < 10
) a where rn = 1

Upvotes: 1

podiluska
podiluska

Reputation: 51494

This returns the result set as detailed.

select tablename.* from tablename
inner join 
(
    select tablename.item, min(tablename.[date]) as mindate 
                from tablename
        inner join (
                        select 
                             item, 
                             max([date]) lastoverdate 
                        from tablename 
                        where val>@const 
                        group by item
                               ) lastover
        on tablename.item = lastover.item
        and tablename.[date]> lastoverdate
    group by tablename.item
) below
    on tablename.item = below.item
    and tablename.date = below.mindate

Upvotes: 3

Related Questions