查询以找到总和最大的最大行数

时间:2018-11-25 07:19:52

标签: sql oracle knapsack-problem subset-sum

我有一个如下表,例如

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

我需要一个查询,该查询可计算并返回最大总行数等于或小于102的行集,例如,选择总行数最接近目标的行集。

如果绑定结果的行数相同且行数相同,则采用具有最高可访问值的组合集

在此示例中,答案为A,B,D,因为它有三个项目,而C,D(也等于102)只有两个。

2 个答案:

答案 0 :(得分:1)

您可以使用递归查询,但是由于这是一个非多项式问题,因此随着更多记录的出现,性能将会下降。第一个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 

答案 1 :(得分:0)

这是我的尝试。 未测试。

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;
/

查询返回最大值<=目标的col。 从目标中减去返回的值,然后我们再次搜索最大值<=新目标。 我们重复此过程,直到新目标为0(已达到原始标记)或没有其他值<=目标为止。 根据需要形成结果并返回。