The simplest way of excluding the current row from the result of an analytic

oracleoracle-10gwindow functions

I want to do something like the following:

with w as ( select level as foo, 
                   decode(level,8,1,mod(level,4))*100 as bar
            from dual connect by level<9 ) 
select foo, bar, 
       min(foo) keep (dense_rank first order by foo) over (partition by bar) as lowest
from w;
/*
FOO                    BAR                    LOWEST                 
---------------------- ---------------------- ---------------------- 
4                      0                      4                      
8                      100                    1                      
5                      100                    1                      
1                      100                    1                      
2                      200                    2                      
6                      200                    2                      
3                      300                    3                      
7                      300                    3     
*/

but I want lowest to show the next lowest if the current row is the lowest for a given bar, or null if it is the only one. In other words I want this result:

/*
FOO                    BAR                    PSEUDOLOWEST                 
---------------------- ---------------------- ---------------------- 
4                      0                                             
1                      100                    5                      
5                      100                    1                      
8                      100                    1                      
2                      200                    6                      
6                      200                    2                      
3                      300                    7                      
7                      300                    3        
*/

What is the simplest way of achieving this?

Best Answer

There may be a simpler way, but here is a solution. If the current row is the minimum foo for the bar, then we always want the next (lead) foo. When there is only one foo for a bar the lead will always be null and for every other case we really do want the minimum foo for the bar.

with w as (select level as foo, decode(level,8,1,mod(level,4))*100 as bar
   from dual connect by level<9) 
select foo, bar
   , case 
        when foo = min(foo) over (partition by bar) 
           then lead(foo) over (partition by bar ORDER BY foo)
        else min(foo) over (partition by bar) 
     end pseudolowest
from w;

Unfortunately rows excluding current row doesn't exist.