Oracle:重复组中的最小值

时间:2013-12-17 11:29:23

标签: sql oracle

我有一张桌子如下。

DATE_WORKED COUNTRY
1-Nov-13    United Kingdom
4-Nov-13    United Kingdom
5-Nov-13    India
6-Nov-13    India
7-Nov-13    India
8-Nov-13    United Kingdom
11-Nov-13   United Kingdom
12-Nov-13   India
13-Nov-13   India
14-Nov-13   India
15-Nov-13   United Kingdom
18-Nov-13   United Kingdom
19-Nov-13   India
20-Nov-13   India
21-Nov-13   India
22-Nov-13   United Kingdom
25-Nov-13   United Kingdom
26-Nov-13   India
27-Nov-13   India
28-Nov-13   India
29-Nov-13   United Kingdom

我希望找到每个国家/地区逗留的start_date和结束日期。

COUNTRY      START_DATE END_Date
United Kingdom      1-Nov-13    4-Nov-13
India               5-Nov-13    7-Nov-13
United Kingdom      8-Nov-13    11-Nov-13
India               12-Nov-13   14-Nov-13
United Kingdom      15-Nov-13   18-Nov-13
India               19-Nov-13   21-Nov-13
United Kingdom      22-Nov-13   25-Nov-13
India               26-Nov-13   28-Nov-13
United Kingdom      29-Nov-13   

请帮我一个SQL查询来实现这一目标。 提前谢谢。

3 个答案:

答案 0 :(得分:6)

使用Tabibitosan

SQL> create table mytable (date_worked,country)
  2  as
  3  select to_date('1-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
  4  select to_date('4-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
  5  select to_date('5-Nov-13','dd-Mon-yy'), 'India' from dual union all
  6  select to_date('6-Nov-13','dd-Mon-yy'), 'India' from dual union all
  7  select to_date('7-Nov-13','dd-Mon-yy'), 'India' from dual union all
  8  select to_date('8-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
  9  select to_date('11-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
 10  select to_date('12-Nov-13','dd-Mon-yy'), 'India' from dual union all
 11  select to_date('13-Nov-13','dd-Mon-yy'), 'India' from dual union all
 12  select to_date('14-Nov-13','dd-Mon-yy'), 'India' from dual union all
 13  select to_date('15-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
 14  select to_date('18-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
 15  select to_date('19-Nov-13','dd-Mon-yy'), 'India' from dual union all
 16  select to_date('20-Nov-13','dd-Mon-yy'), 'India' from dual union all
 17  select to_date('21-Nov-13','dd-Mon-yy'), 'India' from dual union all
 18  select to_date('22-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
 19  select to_date('25-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual union all
 20  select to_date('26-Nov-13','dd-Mon-yy'), 'India' from dual union all
 21  select to_date('27-Nov-13','dd-Mon-yy'), 'India' from dual union all
 22  select to_date('28-Nov-13','dd-Mon-yy'), 'India' from dual union all
 23  select to_date('29-Nov-13','dd-Mon-yy'), 'United Kingdom' from dual
 24  /

Table created.

SQL> with tabibitosan as
  2  ( select row_number() over (order by date_worked)
  3           - row_number() over (partition by country order by date_worked) grp
  4         , date_worked
  5         , country
  6      from mytable
  7  )
  8  select country
  9       , min(date_worked) start_date
 10       , max(date_worked) end_date
 11    from tabibitosan
 12   group by country
 13       , grp
 14   order by start_date
 15  /

COUNTRY        START_DATE          END_DATE
-------------- ------------------- -------------------
United Kingdom 01-11-2013 00:00:00 04-11-2013 00:00:00
India          05-11-2013 00:00:00 07-11-2013 00:00:00
United Kingdom 08-11-2013 00:00:00 11-11-2013 00:00:00
India          12-11-2013 00:00:00 14-11-2013 00:00:00
United Kingdom 15-11-2013 00:00:00 18-11-2013 00:00:00
India          19-11-2013 00:00:00 21-11-2013 00:00:00
United Kingdom 22-11-2013 00:00:00 25-11-2013 00:00:00
India          26-11-2013 00:00:00 28-11-2013 00:00:00
United Kingdom 29-11-2013 00:00:00 29-11-2013 00:00:00

9 rows selected.

答案 1 :(得分:0)

比@ RobVanWijk的回答更复杂:

with v_data as (
  select to_date('2013-11-01', 'YYYY-MM-DD') as date_worked, 'UK' as country from dual union all
  select to_date('2013-11-04', 'YYYY-MM-DD') as date_worked, 'UK' as country from dual union all
  select to_date('2013-11-05', 'YYYY-MM-DD') as date_worked, 'India' as country from dual union all
  select to_date('2013-11-06', 'YYYY-MM-DD') as date_worked, 'India' as country from dual union all
  select to_date('2013-11-07', 'YYYY-MM-DD') as date_worked, 'India' as country from dual union all
  select to_date('2013-11-08', 'YYYY-MM-DD') as date_worked, 'UK' as country from dual union all
  select to_date('2013-11-11', 'YYYY-MM-DD') as date_worked, 'UK' as country from dual union all
  select to_date('2013-11-12', 'YYYY-MM-DD') as date_worked, 'India' as country from dual
)
select country, start_day, end_day from (
  select 
    v3.*, 
    row_number() over (partition by start_day, end_day order by date_worked) as rn
  from (  
    select 
      v2.*,
      max(case when is_first_day = 1 then date_worked else null end) over (Partition by null order by date_worked) as start_day,
      min(case when is_last_day = 1 then date_worked else null end) over (Partition by null order by date_worked desc) as end_day
    from ( 
      select 
        v1.*,
        (case when country <> nvl(country_next_day, 'n/a') then 1 else 0 end) is_last_day,
        (case when country <> nvl(country_prev_day, 'n/a') then 1 else 0 end) is_first_day
      from (   
        select 
          date_worked, 
          country, 
          lead(country) over (order by date_worked) as country_next_day,
          lag(country)  over (order by date_worked) as country_prev_day
        from v_data  
      ) v1
    ) v2
    order by date_worked
  ) v3
) v4 where rn=1

说明:

  • 对于每个工作日,使用lag()和lead()分析函数(v1)获取后继者和前任
  • 对于每个工作日,通过将其国家/地区与上一个和下一个国家/地区进行比较来确定它是组的开头还是结尾(v2)
  • 为每个组计算开始和结束日期(v3)
  • 为每个工作日计算其组内的排序(v4)
  • 仅返回订购1
  • 的工作日

答案 2 :(得分:0)

尝试此查询:

select country,min(date_worked) as start_date,max(date_worked) as end_date
from (select country,date_worked,
    Row_Number() over(order by date_worked)
    -Row_Number() over(partition by country order by date_worked) as disTance
  from YourTable)
group by disTance,country order by min(date_worked);