【问题标题】:Grouping data by name and date ranges按名称和日期范围分组数据
【发布时间】:2018-03-13 16:44:22
【问题描述】:

我的 oracle 表中有数据,其中的名称和日期范围如下:

Name                From        To
Lopes, Janine       07-Jun-17   16-Jul-17
Lopes, Janine       17-Jul-17   23-Jul-17
Lopes, Janine       24-Jul-17   31-Aug-17
Baptista, Maria     23-Dec-16   19-Feb-17
Deyak,Sr, Thomas    22-Jan-17   18-Apr-17
Deyak,Sr, Thomas    27-Apr-17   14-May-17
Deyak,Sr, Thomas    15-May-17   21-May-17
Deyak,Sr, Thomas    22-May-17   28-May-17
Deyak,Sr, Thomas    29-May-17   31-May-17
Serrentino, Joyce   18-Mar-17   30-Apr-17
More, Cathleen      30-Jul-17   13-Aug-17
More, Cathleen      14-Aug-17   20-Aug-17
More, Cathleen      21-Aug-17   27-Aug-17
More, Cathleen      28-Aug-17   03-Sep-17
More, Cathleen      04-Sep-17   10-Sep-17
More, Cathleen      11-Sep-17   24-Sep-17
Barrows, Michael    30-Jan-17   19-Mar-17
Barrows, Michael    20-Mar-17   26-Mar-17
Barrows, Michael    27-Mar-17   02-Apr-17
Barrows, Michael    03-Apr-17   07-Apr-17

大多数情况下,对于一位用户来说,to date 比 from date 大一并且是连续的,但在某些情况下,数据会中断,所以我的输出应该如下所示:

Name                From            To
Lopes, Janine       07-Jun-17   31-Aug-17
Baptista, Maria     23-Dec-16   19-Feb-17
Deyak,Sr, Thomas    22-Jan-17   18-Apr-17
Deyak,Sr, Thomas    27-Apr-17   31-May-17
Serrentino, Joyce   18-Mar-17   30-Apr-17
More, Cathleen      30-Jul-17   24-Sep-17
Barrows, Michael    30-Jan-17   07-Apr-17

如果我执行 min(from) 和 max(to),我会丢失一些记录,例如 Thomas。 我应该怎么写sql来获取数据是我需要的。

【问题讨论】:

  • 这是在 sql server rextester.com/EVU37046 中执行此操作的一种方法。这应该给你一个开始的想法。将函数替换为相关的 oracle 函数
  • 您是根据实际输入的人的总天数还是从From到To的总天数来查找?
  • @Shawn 我希望通过将 from 和 to date 作为 max(from) 和 max(to) 来获取分组,如果日期范围内没有中断但如果有中断我希望它开始一个新的范围
  • 明白了,我起初误读了这个问题,并以为您在寻找天数。您也许可以使用 LEAD 或 LAG 函数。

标签: sql database oracle oracle12c


【解决方案1】:

在 Oracle 12.1 及更高版本中,the MATCH_RECOGNIZE 子句可以快速处理此类要求。我正在使用与我的其他答案相同的设置和模拟数据(WITH 子句),并且输出也相同。

select name, date_fr, date_to
from   inputs
match_recognize(
  partition by name
  order by date_fr
  measures a.date_fr     as date_fr,
           last(date_to) as date_to
  pattern ( a b* )
  define b as date_fr = prev(date_to) + 1
)
;

【讨论】:

  • @Auguster - 我同意这应该最有效如果你有Oracle 12或更高版本(你这样做)。在我意识到你用oracle12c 标记之前,我先写了 Tabibitosan 方法。当我写 match_recognize 解决方案时,另一个已经被投票了,这意味着人们发现它很有帮助,所以我决定保留它(而不是删除)。但在 Oracle 12 上,人们应该使用match_recognize,而不是旧方法。
  • 这是一个非常酷的解决方案。当我与 Oracle 合作时,我有时会遇到一些让我想知道为什么他们不能像 Microsoft TSQL 那样做的事情。然后有这样的事情让我想知道为什么MS不能这样做。
【解决方案2】:

这可以用 Tabibitosan 方法很好地解决。

准备工作:

alter session set nls_date_format = 'dd-Mon-rr';

Session altered.
;

查询(包括为方便起见的模拟输入):

with
     inputs ( name, date_fr, date_to ) as (
       select 'Lopes, Janine'    , to_date('07-Jun-17'), to_date('16-Jul-17') from dual union all
       select 'Lopes, Janine'    , to_date('17-Jul-17'), to_date('23-Jul-17') from dual union all
       select 'Lopes, Janine'    , to_date('24-Jul-17'), to_date('31-Aug-17') from dual union all
       select 'Baptista, Maria'  , to_date('23-Dec-16'), to_date('19-Feb-17') from dual union all
       select 'Deyak,Sr, Thomas' , to_date('22-Jan-17'), to_date('18-Apr-17') from dual union all
       select 'Deyak,Sr, Thomas' , to_date('27-Apr-17'), to_date('14-May-17') from dual union all
       select 'Deyak,Sr, Thomas' , to_date('15-May-17'), to_date('21-May-17') from dual union all
       select 'Deyak,Sr, Thomas' , to_date('22-May-17'), to_date('28-May-17') from dual union all
       select 'Deyak,Sr, Thomas' , to_date('29-May-17'), to_date('31-May-17') from dual union all
       select 'Serrentino, Joyce', to_date('18-Mar-17'), to_date('30-Apr-17') from dual union all
       select 'More, Cathleen'   , to_date('30-Jul-17'), to_date('13-Aug-17') from dual union all
       select 'More, Cathleen'   , to_date('14-Aug-17'), to_date('20-Aug-17') from dual union all
       select 'More, Cathleen'   , to_date('21-Aug-17'), to_date('27-Aug-17') from dual union all
       select 'More, Cathleen'   , to_date('28-Aug-17'), to_date('03-Sep-17') from dual union all
       select 'More, Cathleen'   , to_date('04-Sep-17'), to_date('10-Sep-17') from dual union all
       select 'More, Cathleen'   , to_date('11-Sep-17'), to_date('24-Sep-17') from dual union all
       select 'Barrows, Michael' , to_date('30-Jan-17'), to_date('19-Mar-17') from dual union all
       select 'Barrows, Michael' , to_date('20-Mar-17'), to_date('26-Mar-17') from dual union all
       select 'Barrows, Michael' , to_date('27-Mar-17'), to_date('02-Apr-17') from dual union all
       select 'Barrows, Michael' , to_date('03-Apr-17'), to_date('07-Apr-17') from dual
     )
-- End of simulated inputs (for testing only, not part of the solution).
-- SQL query begins BELOW THIS LINE. Use your actual table and column names.
select name, min(date_fr) as date_fr, max(date_to) as date_to
from   ( select name, date_fr, date_to,
                date_to - sum( date_to - date_fr + 1 ) over (partition by name 
                                                            order by date_fr) as gr
         from   inputs
       )
group by name, gr
order by name, date_fr
;

输出:

NAME              DATE_FR   DATE_TO 
----------------- --------- ---------
Baptista, Maria   23-Dec-16 19-Feb-17
Barrows, Michael  30-Jan-17 07-Apr-17
Deyak,Sr, Thomas  22-Jan-17 18-Apr-17
Deyak,Sr, Thomas  27-Apr-17 31-May-17
Lopes, Janine     07-Jun-17 31-Aug-17
More, Cathleen    30-Jul-17 24-Sep-17
Serrentino, Joyce 18-Mar-17 30-Apr-17

 7 rows selected 

【讨论】:

  • 感兴趣的人:community.oracle.com/docs/DOC-915680 >> 来自最后一段:The one thing to remember though is, if you're considering how to group sequences of values into 'ranges', then think "Tabibitosan" !!。它还说如果您的版本 >= 12c,则使用MATCH_RECOGNIZE。 ^^
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-04
  • 2021-11-16
  • 2021-03-05
  • 2016-12-04
  • 2013-05-19
相关资源
最近更新 更多