Oracle: выбор строк при изменении значения в одном столбце - PullRequest
1 голос
/ 15 апреля 2020

У меня есть следующая таблица:

 PLACE       USER_ID Date
---------- ---------- -----------------------------
        ABC      4     14/04/20 12:05:29,255000000  
        ABC      4     14/04/20 15:42:28,389000000  
        ABC      4     14/04/20 18:33:20,202000000  
        ABC      4     14/04/20 22:51:28,339000000    
        XYZ      4     14/04/20 11:07:23,335000000     
        XYZ      2     14/04/20 12:15:12,123000000    
        ABC      4     13/04/20 22:09:33,255000000   
        QWE      4     13/04/20 10:18:29,144000000 
        XYZ      2     14/04/20 10:05:47,255000000   

И мне нужно получить строки, когда место меняет порядок по дате для выбранного user_id. Таким образом, желаемый результат должен быть таким (для user_id 4):

 PLACE       USER_ID           DATE
---------- ---------- -----------------------------
        ABC      4     14/04/20 12:05:29,255000000     
        XYZ      4     14/04/20 11:07:23,335000000 
        ABC      4     13/04/20 22:09:33,255000000    
        QWE      4     13/04/20 10:18:29,144000000 

Я попытался с минимальной датой, но в моем примере я потеряю данные, если пользователь вернется в это место:

 SELECT MIN(DATE), PLACE FROM user_places WHERE USER_ID=4 GROUP BY PLACE

Результат, который я получаю (пропустив одну строку):

 PLACE       USER_ID           DATE
---------- ---------- -----------------------------
        XYZ      4     14/04/20 11:07:23,335000000 
        ABC      4     13/04/20 22:09:33,255000000    
        QWE      4     13/04/20 10:18:29,144000000 

Заранее спасибо!

Ответы [ 2 ]

2 голосов
/ 15 апреля 2020

В Oracle 12.1 и выше проблемы с пропусками и островками, подобные этой, являются легкой задачей для предложения match_recognize. Например:

Настройка таблицы

alter session set nls_timestamp_format = 'dd/mm/rr hh24:mi:ss,ff';

create table user_places (place, user_id, date_) as 
  select 'ABC', 4, to_timestamp('14/04/20 12:05:29,255000000') from dual union all  
  select 'ABC', 4, to_timestamp('14/04/20 15:42:28,389000000') from dual union all  
  select 'ABC', 4, to_timestamp('14/04/20 18:33:20,202000000') from dual union all
  select 'ABC', 4, to_timestamp('14/04/20 22:51:28,339000000') from dual union all
  select 'XYZ', 4, to_timestamp('14/04/20 11:07:23,335000000') from dual union all
  select 'XYZ', 2, to_timestamp('14/04/20 12:15:12,123000000') from dual union all
  select 'ABC', 4, to_timestamp('13/04/20 22:09:33,255000000') from dual union all
  select 'QWE', 4, to_timestamp('13/04/20 10:18:29,144000000') from dual union all
  select 'XYZ', 2, to_timestamp('14/04/20 10:05:47,255000000') from dual
;

commit;

Запрос и вывод

select place, user_id, date_
from   (select * from user_places where user_id = 4)
match_recognize (
  order    by date_
  all rows per match
  pattern  (a {- b* -} )
  define   b as place = a.place
)
order by date_ desc   --   if needed
;

PLACE  USER_ID  DATE_
-----  -------  ---------------------------
ABC          4  14/04/20 12:05:29,255000000
XYZ          4  14/04/20 11:07:23,335000000
ABC          4  13/04/20 22:09:33,255000000
QWE          4  13/04/20 10:18:29,144000000

Несколько вещей, на которые следует обратить внимание здесь :

  • DATE является зарезервированным ключевым словом. Неверное имя столбца. Я использовал DATE_ вместо этого; обратите внимание на конечное подчеркивание.
  • Я жестко закодировал значение 4 . Конечно, лучшая практика состоит в том, чтобы превратить это в переменную связывания .
  • Если вам действительно нужно сделать это только для одного user_id за один раз, это наиболее эффективно сделать что я сделал - сначала отфильтруйте строки в подзапросе. Однако, если вам нужно сделать это для всех идентификаторов пользователей в одном запросе, вам не нужен подзапрос; Вы выбираете из самой таблицы, и вам нужно добавить partition by user_id в самом начале предложения match_recognize, перед order by date_.
1 голос
/ 15 апреля 2020

Вы можете использовать lag() в подзапросе, чтобы получить «предыдущее» место, а затем отфильтровать строки, где предыдущее место отличается от текущего места:

select place, user_id, date
from (
    select t.*, lag(place) over(partition by user_id order by date) lag_place
    from mytable t
) t
where lag_place is null or place <> lag_place

Это дает ожидаемое вывод для всех пользователей. Если вы хотите только для пользователя 4, то вы можете отфильтровать в подзапросе (и нет необходимости partition by пользователь):

select place, user_id, date
from (
    select t.*, lag(place) over(order by date) lag_place
    from mytable t
    where user_id = 4
) t
where lag_place is null or place <> lag_place
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...