首页 > 解决方案 > Oracle:当一列中的值更改时选择行

问题描述

我有下表:

 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 

提前致谢!

标签: sqloracledategaps-and-islands

解决方案


match_recognize在 Oracle 12.1 及更高版本中,像这样的间隙和孤岛问题对于该子句来说是一件容易的事。例如:

表设置

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,那么执行我所做的最有效 - 首先在子查询中过滤行。但是,如果您需要对同一查询中的所有用户 ID 执行此操作,则不需要子查询;您从表本身中选择,并且您需要在子句partition by user_id顶部的右侧添加 before 。match_recognizeorder by date_

推荐阅读