使用SQL基于有序列表生成累积信息

时间:2013-10-16 16:13:59

标签: oracle

我想基于有序列表生成累积信息。 在下面的例子中,我想基于其他3列生成下雨天。 所以按城镇和白天订购,如果天气干燥则给出零,如果天气下雨则给出累积分数。

程序上这很容易,但感觉应该有一种方法可以直接用sql生成它而我无法理解它。

可能你可以使用分析范围窗口,但我无法弄清楚如何跨3列进行分析。如果存在干扰行则需要重置意味着我不能忽略'干'行并只使用row_number()。

town       day weather  days of rain
Stevenage  1   dry      
Stevenage  2   dry      
Stevenage  3   rain     1
Stevenage  4   rain     2
Stevenage  4   rain     3
Stevenage  5   dry      
Stevenage  6   dry      
Stevenage  8   rain     1
Stevenage  9   rain     2
Stevenage  10  dry      
Watford    1   dry      
Watford    2   dry      
Watford    3   rain     1
Watford    4   rain     2





create table rain_test (town varchar2(20), day number, weather varchar2(10), days_of_rain number);

insert into rain_test(town, day, weather) values ('Stevenage', 1, 'dry');
insert into rain_test(town, day, weather) values ('Stevenage', 2, 'rain');
insert into rain_test(town, day, weather) values ('Stevenage', 3, 'rain');
insert into rain_test(town, day, weather) values ('Stevenage', 4, 'rain');
insert into rain_test(town, day, weather) values ('Stevenage', 5, 'dry');
insert into rain_test(town, day, weather) values ('Stevenage', 6, 'dry');
insert into rain_test(town, day, weather) values ('Stevenage', 7, 'rain');
insert into rain_test(town, day, weather) values ('Stevenage', 8, 'rain');
insert into rain_test(town, day, weather) values ('Stevenage', 9, 'rain');
insert into rain_test(town, day, weather) values ('Stevenage', 10, 'dry');
insert into rain_test(town, day, weather) values ('Watford', 1, 'dry');
insert into rain_test(town, day, weather) values ('Watford', 2, 'dry');
insert into rain_test(town, day, weather) values ('Watford', 3, 'rain');
insert into rain_test(town, day, weather) values ('Watford', 4, 'rain');
commit;

2 个答案:

答案 0 :(得分:1)

使用分析功能,

with x as (
  select town,
         day,
         weather,
         case when weather =                --lag function to find out when the weather changes.
                    lag(weather,1) over (partition by town order by day)
              then 0
              else 1
         end boundary
    from rain_test
),
y as (
  select town, day, weather,
         sum(boundary) over (partition by town order by day) grp    --Sum function to assign a unique group number to a sequence of same weather.
    from x
  )
select town, day, weather,
       case when weather = 'rain'
            then row_number() over (partition by town, grp order by day)    --row_number function to assign unique number to each row in a group.
       end
  from y
order by town, day;

Demo at sqlfiddle

答案 1 :(得分:0)

你去,只是为了挑战,在程序上肯定要容易得多,但不是那么有趣:

select town, day, weather, null days_of_rain
from   rain_test
where  weather = 'dry'
union
select day.town, day.day, weather, day.day - chain.begin_day + 1 days_of_rain
from   rain_test day,
       (
       select town, chain, sum(begin_day) begin_day, sum(end_day) end_day
       from   (/* to find the chains of rainy days */       
              select town, rownum chain, day begin_day, 0 end_day
              from   (/* to find the begining days */
                     select rain.town, rain.day 
                     from   rain_test rain,
                            rain_test day 
                     where  rain.town = day.town
                     and    rain.weather = 'rain'
                     and    day.day = rain.day - 1
                     and    day.weather = 'dry'
                     union /* border condition */
                     select town, min(day) day
                     from   rain_test
                     where  weather = 'rain'
                     group by town
                     order by town, day
                     )
              union all
              select town, rownum chain, 0 begin_day, day end_day
              from   (/* to find the ending days */       
                     select rain.town, rain.day 
                     from   rain_test rain,
                            rain_test day 
                     where  rain.town = day.town
                     and    rain.weather = 'rain'
                     and    day.day = rain.day + 1
                     and    day.weather = 'dry'
                     union /* border condition */
                     select town, max(day) day
                     from   rain_test
                     where  weather = 'rain'
                     group by town
                     order by town, day
                     )    
              )
       group by town, chain   
       ) chain
where  chain.town = day.town
and    chain.begin_day <= day.day
and    chain.end_day >= day.day          
order by town, day