Сопоставление предыдущих и текущих записей С ЗАДЕРЖКОЙ и ОПЕРЕЖЕНИЕМ и БЕЗ них

#sql #sql-server

#sql #sql-сервер

Вопрос:

У меня есть таблица, подобная приведенной ниже. Записи не имеют первичного ключа, и я хочу добиться этого как С ПОМОЩЬЮ, так и БЕЗ функции ЗАДЕРЖКИ и ОПЕРЕЖЕНИЯ.

 ID      ENTID     INOUTDATE             YEAR    MONTH   STATUS
1923    1923    [NULL]                  2099     12      Out
1923    10690   [NULL]                  2099     12      Out
1923    9670    2012-08-24 00:00:00     2012     8       In
1923    1923    2013-06-01 00:00:00     2013     6       In
1923    9670    2018-04-19 00:00:00     2018     4       Out
1923    10690   2019-02-01 00:00:00     2019     2       In
 

И я хочу получить записи, как указано ниже.

 ID      ENTID     INOUTDATE             YEAR    MONTH   STATUS
1923    10690   [NULL]                  2099    12      Out
1923    9670    2012-08-24 00:00:00     2012    8       In
1923    9670    2018-04-19 00:00:00     2018    4       Out
1923    10690   2019-02-01 00:00:00     2019    2       In
 

Комментарии:

1. Почему вы хотите избежать lad() / lead() ? Какова логика фильтрации, которую вы хотите выполнить?

2. Привет, Гордон, я отредактировал свой вопрос. Если возможно, можете ли вы показать оба способа. Спасибо.

3. @GordonLinoff Вероятно, чтобы они могли поддерживать версии старше, чем когда LAG() / LEAD() были введены.

Ответ №1:

lag() это самый простой метод:

 select t.*
from (select t.*, 
             lag(status) over (partition by id, (case when inoutdate is null then 1 else 2 end)
                               order by inoutdate
                              ) as prev_status
      from t
     ) t
where prev_status is null or prev_status <> status;
 

Вы можете рассматривать это как проблему группы и островов, идентифицируя острова с помощью row_number() . Логика более сложная:

 select t.*
from (select t.*, 
             row_number() over (partition by id, (case when inoutdate is null then 1 else 2 end), status, (seqnum - seqnum_s)
                                order by inoutdate
                               ) as seqnum_g
      from (select t.*,
                   row_number() over (partition by id, (case when inoutdate is null then 1 else 2 end) order by inoutdate) as seqnum,
                   row_number() over (partition by id, (case when inoutdate is null then 1 else 2 end), status order by inoutdate) as seqnum_s
            from t
           ) t
     ) t
where seqnum_g = 1;