简体   繁体   中英

Using the date from the next row as the end date of the current row

I have a table laid out as follows:

ID | FOCUS      | DATE
----------------------------
 1 | equipment  | 2018-01-01
 2 | uniform    | 2018-02-14
 3 | attendance | 2018-04-03

The rows are added as new focuses are introduced.

I want to be able to get the start date and end date for a particular focus, assuming it ends as the next one starts. Procuding a view such as:

ID | FOCUS      | STARTDATE  | ENDDATE
--------------------------------------
 1 | equipment  | 2018-01-01 | 2018-02-14
 2 | uniform    | 2018-02-14 | 2018-04-03
 3 | attendance | 2018-04-03 | NULL

The database I'm using has no LEAD or LAG functions, so I need a way to do it in pure SQL. All solutions I've found so far use LEAD and LAG. Any ideas?

You can get the result you want by LEFT JOIN ing the table to itself, on the right table's date being the minimum date value greater than the left table's date. This query will work regardless of ID values being consecutive:

SELECT t1.ID, t1.FOCUS, t1.DATE AS STARTDATE, t2.DATE AS ENDDATE
FROM table1 t1
LEFT JOIN table1 t2 ON t2.DATE = (SELECT MIN(DATE)
                                  FROM table1 t3
                                  WHERE t3.DATE > t1.DATE)

Output:

ID  FOCUS       STARTDATE   ENDDATE
1   equipment   2018-01-01  2018-02-14
2   uniform     2018-02-14  2018-04-03
3   attendance  2018-04-03  null

Demo on dbfiddle

If it's possible that two events might end on the same day, you can modify the query like the following, which will work as long as ID values increase as DATE values increase.

SELECT DISTINCT t1.ID, t1.FOCUS, t1.DATE AS STARTDATE, t2.DATE AS ENDDATE
FROM table1 t1
LEFT JOIN table1 t2 ON t2.DATE = (SELECT MIN(DATE)
                                  FROM table1 t3
                                  WHERE t3.DATE >= t1.DATE AND t3.ID > t1.ID)

Demo on dbfiddle

this should do it

select t.id, t.focus, t.date as startdate, t2.date as enddate 
from 
   (select focus, date, row_number() over (order by date asc)  as rn 
    from tableA) t
left join 
   (select focus, date, row_number() over (order by date asc) as rn from tableA) t2
       on t2.rn + 1 = t.rn

Without LEAD and LAG, you can write it with a subquery that would fetch a date from an ID+1 row, like this:

SELECT qry.id
     , qry.focus
     , qry.date as startdate
     , (select sbq.date from your_table sbq where sbq.id = (qry.id + 1)) as enddate
  FROM your_table qry

I would just use a correlated subquery:

select t.*,
       (select min(t2.date)
        from t t2
        where t2.date > t.date
       ) as enddate
from t;

If you have dates that are the same, you can use id to distinguish the values. That would be:

select t.*,
       (select t2.date
        from t t2
        where t2.date > t.date or
              (t2.date = t.date and t2.id > t.id)
        order by t2.date asc, t2.id asc
        fetch first 1 row only
       ) as enddate
from t;

Note: You don't specify the database you are using. fetch first is standard SQL; some databases use limit , others use select top .

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