Oracle SQL: выберите по крайней мере первые n строк, продолжайте, пока значение столбца не будет отличаться от последнего - PullRequest
2 голосов
/ 25 июля 2011

с учетом таблицы foo следующей структуры (Oracle 11g):

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

Я хочу выбрать первые n строк (упорядоченных по идентификатору) или более, чтобы я всегда получал полную группу.

Пример:

n = 2: я хочу получить хотя бы первые две строки, но поскольку идентификатор 3 также принадлежит группе 100, я хочу получить и это.

n = 4: дайте мне первые четыре строки, и я счастлив; -)

n = 5: запрашиваются строки 1-6.

Ваша помощь очень важна!

Ответы [ 4 ]

7 голосов
/ 25 июля 2011

Решение с использованием rank():

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

Данные испытаний здания:

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>

Запуск ...

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.

РЕДАКТИРОВАНИЕ Вот версия, включающая предложение for update (: 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
0 голосов
/ 25 июля 2011

Если мы предположим, что group_id являются смежными и восходящими, то ответ @ Shannon работает отлично. Если мы не делаем этого предположения, и у нас есть данные, которые выглядят так, например:

SQL> select * from foo order by id;

ID GROUP_ID
-- --------
 1      100
 2      100
 3      100
 4      200
 6      100
 7      400
 9      500
10      500
11      500
12      600

Тогда это более сложная проблема. Например, если N = 3, 4 или 5, то нам нужно получить строки через ID = 6. Для N = 6 нам нужно до ID = 7. Для N = 7 нам нужно через ID = 11.

Я считаю, что этот запрос работает независимо от порядка group_id:

для N = 7:

WITH q AS (SELECT ID, group_id
                , row_number() OVER (ORDER BY ID) rn
                , MAX(id) OVER (PARTITION BY group_id) rn2
             FROM foo)
SELECT ID, group_id FROM q
 WHERE ID <= (SELECT max(rn2) FROM q WHERE rn <= :N)
 ORDER BY ID; 

ID GROUP_ID
-- --------
 1      100
 2      100
 3      100
 4      200
 6      100
 7      400
 9      500
10      500
11      500

9 rows selected

для N = 6:

ID GROUP_ID
-- --------
 1      100
 2      100
 3      100
 4      200
 6      100
 7      400

Для N = 1:

ID GROUP_ID
-- --------
 1      100
 2      100
 3      100
 4      200
 6      100
0 голосов
/ 25 июля 2011

Если всегда верно, что GROUP_ID является непрерывным и восходящим, то это легко решить с помощью SQL с помощью аналитической функции ROW_NUMBER():

SQL> select id
  2         , group_id
  3  from foo
  4  where group_id <= ( select group_id
  5                     from (
  6                              select f.group_id
  7                                     , row_number() over (order by f.id asc) rn
  8                              from foo f
  9                              )
 10                          where rn = &n )
 11  order by id
 12  /
Enter value for n: 2
old  10:                         where rn = &n )
new  10:                         where rn = 2 )

        ID   GROUP_ID
---------- ----------
         1        100
         2        100
         3        100

SQL> r
  1  select id
  2         , group_id
  3  from foo
  4  where group_id <= ( select group_id
  5                     from (
  6                              select f.group_id
  7                                     , row_number() over (order by f.id asc) rn
  8                              from foo f
  9                              )
 10                          where rn = &n )
 11* order by id
Enter value for n: 4
old  10:                         where rn = &n )
new  10:                         where rn = 4 )

        ID   GROUP_ID
---------- ----------
         1        100
         2        100
         3        100
         4        200

SQL> r
  1  select id
  2         , group_id
  3  from foo
  4  where group_id <= ( select group_id
  5                     from (
  6                              select f.group_id
  7                                     , row_number() over (order by f.id asc) rn
  8                              from foo f
  9                              )
 10                          where rn = &n )
 11* order by id
Enter value for n: 5
old  10:                         where rn = &n )
new  10:                         where rn = 5 )

        ID   GROUP_ID
---------- ----------
         1        100
         2        100
         3        100
         4        200
         5        300
         6        300

6 rows selected.

SQL>
0 голосов
/ 25 июля 2011

Если ваши ID всегда последовательны (без пробелов) из 1. И если ваши Group_ID никогда не встречаются в качестве второй группы в другом месте.И если ваши Group_ID s всегда возрастают в значении ...

SELECT
  *
FROM
  foo
WHERE
  Group_ID <= (SELECT Group_ID FROM foo WHERE ID = n)
ORDER BY
  ID

Вы выиграете здесь от наличия отдельных индексов для ID и Group_ID

...