Как выбрать до x строк, когда записи должны быть сгруппированы на основе критериев - PullRequest
2 голосов
/ 17 января 2020

Я хочу создать запрос, в котором я выбираю до x строк, записи группируются по одному идентификатору, и результатом запроса должны быть целые группы. Значения, на которых основывается фильтрация, хранятся в столбце p_id, строки с одинаковыми значениями создают группы. В случае этой таблицы:

    p_id    age
0   00170   64  
1   00170   64  
2   00201   24  
3   00201   64  
4   00201   64  
5   00300   24  
6   00300   20  

Я хочу выбрать 4 строки, но поскольку группы с p_id 00170,00201 - это всего 5 записей, я получаю:

0   00170   64 
1   00170   64

Если бы я выберите 5 строк, которые я получу:

0   00170   64 
1   00170   64 
2   00201   24 
3   00201   64 
4   00201   64 

Если бы я выбрал 6 строк, я бы получил (p_id 00300 - 2 записи, поэтому не включены, так как сумма превышает 6):

0   00170   64 
1   00170   64 
2   00201   24 
3   00201   64 
4   00201   64 

Так целые группы возвращаются. Я работаю с oracle db, с помощью ROWNUM легко выбрать x строк. Я теряюсь, когда пытаюсь получить до определенного количества строк с дополнительными критериями.

Ответы [ 4 ]

3 голосов
/ 17 января 2020

Oracle Настройка :

CREATE TABLE test_data ( p_id, age ) AS
SELECT '00170', 64 FROM DUAL UNION ALL
SELECT '00170', 64 FROM DUAL UNION ALL
SELECT '00201', 24 FROM DUAL UNION ALL
SELECT '00201', 64 FROM DUAL UNION ALL
SELECT '00201', 64 FROM DUAL UNION ALL
SELECT '00300', 24 FROM DUAL UNION ALL  
SELECT '00300', 20 FROM DUAL;

Запрос :

Упорядочить строки, затем найти максимальное число строк для каждой группы и затем отфильтруйте, чтобы вернуть только те группы, чей максимальный номер строки содержится в желаемом предельном числе строк:

SELECT p_id,
       age
FROM   (
  SELECT t.*,
         MAX( ROWNUM ) OVER ( PARTITION BY p_id ) AS grp
  FROM   (
    SELECT *
    FROM   test_data
    ORDER BY p_id
  ) t
)
WHERE  grp <= 4;

Вывод :

P_ID  | AGE
:---- | --:
00170 |  64
00170 |  64

Если вы измените последнюю строку на 5, то она вернет 5 строк и изменит ее на 6, тогда она все равно вернет 5 строк.

db <> fiddle здесь

2 голосов
/ 17 января 2020

Я бы решил это с помощью счетчика окон и фильтрации:

select p_id, age
from (select p_id, age, count(*) over(order by p_id) cnt from mytable t) t
where cnt <= 5
order by p_id

При необходимости вы можете изменить cnt <= 5.

Демонстрация на DB Fiddle :

cnt <= 4:

P_ID | AGE
---: | --:
 170 |  64
 170 |  64

cnt <= 5:

P_ID | AGE
---: | --:
 170 |  64
 170 |  64
 201 |  24
 201 |  64
 201 |  64

cnt <= 6:

P_ID | AGE
---: | --:
 170 |  64
 170 |  64
 201 |  24
 201 |  64
 201 |  64
0 голосов
/ 17 января 2020

GMB ответ в порядке. Но это можно упростить с помощью RANK(). Эта функция выполняет точно то, что вы хотите:

select p_id, age
from (select t.*,
             rank() over (order by p_id) as rnk
      from t
     ) t
where rnk <= 5
order by p_id;

Что еще более важно, хотя, если значения p_id не упорядочены, то вам может потребоваться дополнительный шаг: назначить минимальное значение некоторого столбца заказа для каждого p_id. Позвольте мне назвать этот столбец заказа id:

select p_id, age
from (select t.*,
             rank() over (order by p_id_grp) as rnk
      from (select t.*, min(id) over (partition by p_id) as p_id_grp
            from t
           ) t
     ) t
where rnk <= 5
order by p_id;
0 голосов
/ 17 января 2020

Это типичный запрос Top-N:

используйте ROWNUM с упорядоченным видом для получения правильного порядка:

SELECT p_id, age
FROM   (SELECT p_id, age
        FROM   table
        ORDER BY age DESC)
WHERE ROWNUM <= 4;

Для Oracle v 12 c и далее есть новое предложение FETCH:

SELECT p_id, age
FROM   table
GROUP BY p_id
FETCH FIRST 4 ROWS ONLY;

Дополнительные ресурсы: https://oracle-base.com/articles/misc/top-n-queries

...