Вот способ сделать это на чистом SQL.Я не буду клясться, что нет лучшего способа.
По сути, он использует рекурсивное общее табличное выражение (т. Е. WITH costed...
) для вычисления каждой возможной комбинации элементов на общую сумму менее 20 000 000.
Затем он получает первый полный путь из этого результата.
Затем он получает все строки в этом пути.
ПРИМЕЧАНИЕ: логика предполагает, что ни один id
не длиннее, чем5 цифр.Это LPAD(id,5,'0')
материал.
WITH costed (id, cost, priority, running_cost, path) as
( SELECT id, cost, priority, cost running_cost, lpad(id,5,'0') path
FROM a_test_table
WHERE cost <= 20000000
UNION ALL
SELECT a.id, a.cost, a.priority, a.cost + costed.running_Cost, costed.path || '|' || lpad(a.id,5,'0')
FROM costed, a_test_table a
WHERE a.priority < costed.priority
AND a.cost + costed.running_cost <= 20000000),
best_path as (
SELECT *
FROM costed c
where not exists ( SELECT 'longer path' FROM costed c2 WHERE c2.path like c.path || '|%' )
order by path
fetch first 1 row only )
SELECT att.*
FROM best_path cross join a_test_table att
WHERE best_path.path like '%' || lpad(att.id,5,'0') || '%'
order by att.priority desc;
+----+----------+----------+
| ID | COST | PRIORITY |
+----+----------+----------+
| 1 | 1000000 | 10 |
| 2 | 10000000 | 9 |
| 3 | 5000000 | 8 |
| 7 | 2000000 | 4 |
+----+----------+----------+
ОБНОВЛЕНИЕ - более короткая версия
Эта версия использует MATCH_RECOGNIZE
, чтобы найти все строки в лучшей группепосле рекурсивного CTE:
WITH costed (id, cost, priority, running_cost, path) as
( SELECT id, cost, priority, cost running_cost, lpad(id,5,'0') path
FROM a_test_table
WHERE cost <= 20000000
UNION ALL
SELECT a.id, a.cost, a.priority, a.cost + costed.running_Cost, costed.path || '|' || lpad(a.id,5,'0')
FROM costed, a_test_table a
WHERE a.priority < costed.priority
AND a.cost + costed.running_cost <= 20000000)
search depth first by priority desc set ord
SELECT id, cost, priority
FROM costed c
MATCH_RECOGNIZE (
ORDER BY path
MEASURES
MATCH_NUMBER() AS mno
ALL ROWS PER MATCH
PATTERN (STRT ADDON*)
DEFINE
ADDON AS ADDON.PATH = PREV(ADDON.PATH) || '|' || LPAD(ADDON.ID,5,'0')
)
WHERE mno = 1
ORDER BY priority DESC;
ОБНОВЛЕНИЕ - Еще более короткая версия, с использованием умной идеи из SQL * Server link ОП опубликовала
* Редактировать: удалено использование ROWNUM=1
в якорной части рекурсивного CTE, поскольку оно зависит от произвольного порядка, в котором будут возвращаться строки.Я удивлен, что никто не обмолвился меня об этом.*
WITH costed (id, cost, priority, running_cost) as
( SELECT id, cost, priority, cost running_cost
FROM ( SELECT * FROM a_test_table
WHERE cost <= 20000000
ORDER BY priority desc
FETCH FIRST 1 ROW ONLY )
UNION ALL
SELECT a.id, a.cost, a.priority, a.cost + costed.running_Cost
FROM costed CROSS APPLY ( SELECT b.*
FROM a_test_table b
WHERE b.priority < costed.priority
AND b.cost + costed.running_cost <= 20000000
FETCH FIRST 1 ROW ONLY
) a
)
CYCLE id SET is_cycle TO 'Y' DEFAULT 'N'
select id, cost, priority from costed
order by priority desc