Search code examples
sqloraclerownum

Oracle SQL: Select at least the first n rows, continue until a column value is different from the last one


given a table foo of the following structure (Oracle 11g):

ID | GROUP_ID
 1 | 100
 2 | 100
 3 | 100
 4 | 200
 5 | 300
 6 | 300
 7 | 400

I want to select the first n rows (ordered by ID) or more, such that I always get a complete group.

Example:

n = 2: I want to get at least the first two rows, but since ID 3 also belongs to group 100, I want to get that as well.

n = 4: Give me the first four rows and I am happy ;-)

n = 5: Rows 1-6 are requested.

Your help is highly appreciated!


Solution

  • Solution using rank():

    select id, group_id
    from (select t.*, rank() over (order by group_id) as rnk
        from t)
    where rnk <= :n;
    

    Building test data:

    SQL> create table t (id number not null primary key
      2      , group_id number not null);
    
    Table created.
    
    SQL> insert into t values (1, 100);
    
    1 row created.
    
    SQL> insert into t values (2, 100);
    
    1 row created.
    
    SQL> insert into t values (3, 100);
    
    1 row created.
    
    SQL> insert into t values (4, 200);
    
    1 row created.
    
    SQL> insert into t values (5, 300);
    
    1 row created.
    
    SQL> insert into t values (6, 300);
    
    1 row created.
    
    SQL> insert into t values (7, 400);
    
    1 row created.
    
    SQL> commit;
    
    Commit complete.
    SQL>
    

    Running...

    SQL> var n number
    SQL> exec :n := 2;
    
    PL/SQL procedure successfully completed.
    
    SQL> select id, group_id
      2  from (select t.*, rank() over (order by group_id) as rnk
      3      from t)
      4  where rnk <= :n;
    
            ID   GROUP_ID
    ---------- ----------
             1        100
             2        100
             3        100
    
    SQL> exec :n := 4;
    
    PL/SQL procedure successfully completed.
    
    SQL> select id, group_id
      2  from (select t.*, rank() over (order by group_id) as rnk
      3      from t)
      4  where rnk <= :n;
    
            ID   GROUP_ID
    ---------- ----------
             1        100
             2        100
             3        100
             4        200
    
    SQL> exec :n := 5;
    
    PL/SQL procedure successfully completed.
    
    SQL> select id, group_id
      2  from (select t.*, rank() over (order by group_id) as rnk
      3      from t)
      4  where rnk <= :n;
    
            ID   GROUP_ID
    ---------- ----------
             1        100
             2        100
             3        100
             4        200
             5        300
             6        300
    
    6 rows selected.
    

    EDIT Here is version that includes the for update clause (:n = 2):

    SQL> select id, group_id
      2  from T
      3  where rowid in (select RID
      4      from (select t.rowid as RID, t.*, rank() over (order by group_id) as rnk
      5          from t)
      6      where rnk <= :n)
      7  for update;
    
            ID   GROUP_ID
    ---------- ----------
             1        100
             2        100
             3        100