Запрос, чтобы найти наибольшее количество строк с наибольшей суммой - PullRequest
0 голосов
/ 25 ноября 2018

У меня есть таблица, подобная приведенной ниже, например,

A.    1
B.    2
C.    3
D.    99
E.    90

Мне нужен запрос, который вычисляет и возвращает набор наибольшего числа строк, которые равны или меньше, например, 102, выбирая набор с итогом, ближайшим к цели.

Если есть связанные результаты, которые имеют одинаковое общее количество и одинаковое количество строк, возьмите набор с наибольшим единственным значением Accessible

В этом примере ответом является A,B,D, потому что у него есть три элемента, а у C,D (который также составляет 102) только два.

Ответы [ 2 ]

0 голосов
/ 25 ноября 2018

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

with tbl(key, value) as (
    select 'A',  1 from dual union all
    select 'B',  2 from dual union all
    select 'C',  3 from dual union all
    select 'D', 99 from dual union all
    select 'E', 90 from dual
),
rec(greatest_key, greatest_single, key_count, keys, total) as (
    select     key,
               value,
               1,
               key, 
               value
    from       tbl
    union all
    select     tbl.key,
               greatest(tbl.value, rec.greatest_single),
               rec.key_count+1,
               substr(rec.keys, 0, 1000) || ', ' || tbl.key,
               rec.total + tbl.value
    from       rec 
    inner join tbl 
            on tbl.key > rec.greatest_key
           and rec.total + tbl.value <= 102
),
ordered(total, keys, r) as (
    select   total, keys, row_number() over ( 
                          order by total desc, key_count desc, greatest_single desc)
    from     rec
)
select total, keys
from   ordered
where  r = 1

Часть ordered предназначена только для получения "верхней" записи.

Смотрите, как она работает на rextester .

Если у вас Oracle 12c +, вы можете завершить запрос без использования ordered:

select   total, keys
from     rec
order by total desc, 
         key_count desc
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY 
0 голосов
/ 25 ноября 2018

Вот моя попытка. НЕ ИСПЫТАНО.

FUNCTION find_min_set ( p_target IN NUMBER ) RETURN VARCHAR2 IS
  CURSOR c1 ( p_find_val NUMBER ) IS
    SELECT the_id, the_val
    FROM   the_table t1
    WHERE  the_val = ( SELECT MAX( the_val ) the_val
                       FROM   the_table
                       WHERE  the_val <= p_find_val ) ;
  l_target NUMBER;
  l_id     VARCHAR2(16);
  l_val    NUMBER;
  l_ret_List  VARCHAR2(4000) := '';
BEGIN
   l_target := p_target;
   WHILE l_target > 0 LOOP
      OPEN c1 ( l_target );
      FETCH c1 INTO l_id, l_val;
      EXIT WHEN c1%NOTFOUND;
      CLOSE c1;
      l_ret_list := l_ret_list || ' ' || l_id;
      l_target := l_target - l_val;
   END LOOP;
   l_ret_list := 'Target: ' || p_target || ',' ||
                  'Remaidner: ' || l_target ||  ', ' ||
                  'List: ' || l_ret_list;
   RETURN ( l_ret_list );
END;
/

Запрос возвращает столбец с наибольшим значением <= цель.Возвращаемое значение вычитается из цели, и мы снова ищем наибольшее значение <= новой цели.Мы повторяем это до тех пор, пока новая цель не станет равной 0 (оригинальный тагер был достигнут) или пока не исчезнут значения <= target.Сформируйте результаты по мере необходимости и верните. </p>

...