1

我想根据有序列表生成累积信息。在下面的示例中,我想根据其他 3 列生成下雨天数。因此,按城镇和日期排序,如果天气干燥,则为 null,如果天气下雨,则为累积分数。

程序上这很容易,但感觉应该有一种方法可以直接用 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;
4

2 回答 2

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

于 2013-10-17T05:41:50.287 回答
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
于 2013-10-17T03:28:16.903 回答