CREATE TABLE test_ ( num_ NUMBER);
INSERT INTO test_ VALUES (12);
INSERT INTO test_ VALUES (3);
INSERT INTO test_ VALUES (12);
INSERT INTO test_ VALUES (8);
INSERT INTO test_ VALUES (10);
INSERT INTO test_ VALUES (4);
INSERT INTO test_ VALUES (4);
INSERT INTO test_ VALUES (2);
INSERT INTO test_ VALUES (7);
INSERT INTO test_ VALUES (10);
INSERT INTO test_ VALUES (10);
INSERT INTO test_ VALUES (11);
INSERT INTO test_ VALUES (12);
INSERT INTO test_ VALUES (11);
INSERT INTO test_ VALUES (4);
COMMIT;
CREATE OR REPLACE TYPE T_NUMBER AS TABLE OF NUMBER;
DECLARE
lv_numbers T_NUMBER;
lv_result VARCHAR2(32000);
/* Convert collection of number to VARCHAR2
*
* @param pi_numbers collection to process
* @param pi_separator separator
* @return pi_numbers converted to VARCHAR2 with pi_separator as separator
*/
FUNCTION toVarchar2(
pi_numbers T_NUMBER,
pi_separator VARCHAR2 DEFAULT ','
)
RETURN VARCHAR2
IS
lv_result VARCHAR2(4000);
BEGIN
FOR i IN 1..pi_numbers.COUNT LOOP
lv_result := lv_result || pi_numbers(i) || pi_separator;
END LOOP;
lv_result := RTRIM(lv_result, pi_separator);
RETURN lv_result;
END toVarchar2;
/* Roll collection to left
*
* @param pi_numbers collection to process
* @return rolled colletion
*/
FUNCTION rollLeft(
pi_numbers T_NUMBER
)
RETURN T_NUMBER
IS
lv_result T_NUMBER;
lv_currentTop NUMBER;
BEGIN
lv_result := pi_numbers;
IF lv_result.COUNT > 1 THEN
lv_currentTop := lv_result(1);
FOR i IN 2..lv_result.COUNT LOOP
lv_result(i-1) := lv_result(i);
END LOOP;
lv_result(lv_result.COUNT) := lv_currentTop;
END IF;
RETURN lv_result;
END rollLeft;
/* Split collection to a grup in which we have items with SUM equal to pi_sum
*
* @param pi_numbers collection to process
* @param pi_sum our goal sum
* @return result of split
*/
FUNCTION split_(
pi_numbers T_NUMBER,
pi_sum NUMBER
)
RETURN VARCHAR2
IS
lv_groupSeparator CONSTANT VARCHAR2(1) := '|';
lv_result VARCHAR2(4000);
lv_numbersInGroup T_NUMBER := new T_NUMBER();
lv_toFartherSplit T_NUMBER;
lv_currentSum NUMBER := 0;
lv_allProcessed BOOLEAN := FALSE;
BEGIN
IF pi_numbers.COUNT > 0 THEN
lv_currentSum := pi_numbers(1);
lv_numbersInGroup.EXTEND;
lv_numbersInGroup(lv_numbersInGroup.COUNT) := pi_numbers(1);
FOR i IN 2..pi_numbers.COUNT LOOP
IF lv_currentSum + pi_numbers(i) <= pi_sum THEN
lv_currentSum := lv_currentSum + pi_numbers(i);
lv_numbersInGroup.EXTEND;
lv_numbersInGroup(lv_numbersInGroup.COUNT) := pi_numbers(i);
END IF;
IF lv_currentSum = pi_sum THEN
EXIT;
END IF;
END LOOP;
IF lv_currentSum = pi_sum THEN
lv_result := toVarchar2(lv_numbersInGroup) || lv_groupSeparator;
lv_toFartherSplit := pi_numbers MULTISET EXCEPT lv_numbersInGroup;
ELSE
lv_toFartherSplit := rollLeft(pi_numbers => pi_numbers);
IF lv_toFartherSplit(lv_toFartherSplit.COUNT) < lv_toFartherSplit(1) THEN
lv_result := 'NotUsed:' || toVarchar2(pi_numbers);
lv_allProcessed := TRUE;
END IF;
END IF;
IF lv_allProcessed = FALSE THEN
lv_result := lv_result ||
split_(
pi_numbers => lv_toFartherSplit,
pi_sum => pi_sum
);
END IF;
END IF;
RETURN lv_result;
END split_;
BEGIN
SELECT num_
BULK COLLECT INTO lv_numbers
FROM TEST_
ORDER BY num_ desc;
lv_result := split_(
pi_numbers => lv_numbers,
pi_sum => 30
);
dbms_output.put_line(lv_result);
END;
/