Anda di halaman 1dari 4

9/14/2019 Ask TOM "How to do a combination to sum to find all the opt...

"

Ask TOM 

Questions  Office Hours  Resources  About

Questions \ How to do a combination to sum to find all the options up to a certain value

Thanks for the question, Juan Carlos.  Latest Followup  


Asked: August 10, 2016 - 9:33 pm UTC
Answered by: Chris Saxon - Last updated: August 12, 2016 - 3:19 am UTC
Category: Database - Version: 12.1.0.1
Viewed 1000+ times

 Whilst you are here, check out some content from the AskTom team: Histograms on data
(not the optimizer)

You Asked

Hello TomConChris

I can have up to 7 (in reality 50) values, and I must sum all the combination and choose all the combination that get an
amount
In example All the combinations with the numbers to get a pizza of 5 kg.
option 1 5Kg,
Option 2 3Kg
Option 3 2Kg
Option 4 4Kg
In the previous example I had tow combionatios (option1 5=5) (options2 and option3 2+3=5) both givesme 5.

In the following example I have a way to get all the combinations, in the previous case (option2 +option3=5), but I can't find
the option1=5, because all the combinations returns a value, and I need always they include the option, I had done a several
test and I couldn't then I thought why to do it, if TomConChris can :)

WITH COMBINACION AS
( SELECT 1 C, 1 Q
FROM DUAL
UNION ALL
SELECT 2 C, 2 Q
FROM DUAL)
SELECT T1.C C1,T2.C C2,T3.C C3,T4.C C4,T5.C C5,T6.C C6,T7.C C7
FROM COMBINACION T1
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:9531157800346395886 1/5
9/14/2019 Ask TOM "How to do a combination to sum to find all the opt..."

LEFT OUTER JOIN COMBINACION T2 ON (T2.C NOT IN (T1.C) )


Ask TOM
LEFT OUTER JOIN COMBINACION T3 ON (T3.C NOT IN (T1.C,T2.C) ) 
LEFT OUTER JOIN COMBINACION T4 ON (T4.C NOT IN (T1.C,T2.C,T3.C) )
Questions Office
 JOIN
LEFT OUTER Hours T5 ON
COMBINACION Resources About
(T1.C,T2.C,T3.C,T4.C)
(T5.C NOT IN )
LEFT OUTER JOIN COMBINACION T6 ON (T6.C NOT IN (T1.C,T2.C,T3.C,T4.C,T5.C) )
LEFT OUTER JOIN COMBINACION T7 ON (T7.C NOT IN (T1.C,T2.C,T3.C,T4.C,T5.C,T6.C) );

This resturns
12
21

I need it inclues all the times a 0 in all the combinations, to get the situations when only one is enough (in reality it has 50
elements)

10
12
20
21

if it where three elements


123
132
213
231
312
321

I would need

100
120
123
020
etc.

I tried things like LEFT OUTER JOIN COMBINACION T2 ON (T2.C NOT IN (T1.C) OR T2.C=0) , but I think I'm misunderstanding
the working of the LEFT OUTER JOIN, because I never used, truely.

Thank you :) in advance, hope you can solve it :)

and we said...

Eeek! Joining like that limits the number of things you can compare to the number of joins. And it's terrible for performance
as your tables have more rows.

There is another way. Recursive SQL.

https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:9531157800346395886 2/5
9/14/2019 Ask TOM "How to do a combination to sum to find all the opt..."

Ask
This will maintain two things TOM 

Questions Office
 weight
- The total Hours 
so far Resources  About
- A list of the pizza toppings you've already checked

Obviously we need the weight, because that's what we're measuring. The list is to prevent us checking the same row twice.

So we want to start with all the rows in the table. You could exclude those <= 5kg at this point if necessary. Wrap the
topping with hashes. This ensures 1 is not confused with 11, 21, etc. (#1# <> #11#, but 1# is part of 11#, 21# and so on).

select topping, '#' || topping || '#', weight tot_wt from t

To build the recursive case, we now:

Join the original table where:

- The topping is not in the list already considered


- The total weight + new weight <= 5
- The current topping number is greater than those considered. This is to prevent double counting 2,3 and 3,2

For each row that passes these checks, append its topping to the list of those already processed and increment the total
weight:

select t.topping, toppings || t.topping || '#', tot_wt + weight tot_wt


from tots
join t
on instr(toppings, '#' || t.topping || '#') = 0
and tot_wt + weight <= 5
and tots.topping < t.topping

Put it all together and you have:

create table t (
topping int,
weight int
);

insert into t values ( 1, 5 );


insert into t values ( 2, 3 );
insert into t values ( 3, 2 );
insert into t values ( 4, 4 );

with tots (topping, toppings, tot_wt) as (


select topping, '#' || topping || '#', weight tot_wt from t
union all
select t.topping, toppings || t.topping || '#', tot_wt + weight tot_wt
from tots
join t
on instr(toppings, '#' || t.topping || '#') = 0
and tot_wt + weight <= 5
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:9531157800346395886 3/5
9/14/2019 Ask TOM "How to do a combination to sum to find all the opt..."
and tots.topping < t.topping
) Ask TOM 
select toppings, tot_wt from tots;

Questions 
TOPPINGS Office
TOT_WT Hours  Resources  About
#1# 5
#2# 3
#3# 2
#4# 4
#2#3# 5

This gets you all the combinations that weight up to 5kg. Add a 1kg option and it still works:

insert into t values ( 5, 1 );

with tots (topping, toppings, tot_wt) as (


select topping, '#' || topping || '#', weight tot_wt from t
union all
select t.topping, toppings || t.topping || '#', tot_wt + weight tot_wt
from tots
join t
on instr(toppings, '#' || t.topping || '#') = 0
and tot_wt + weight <= 5
and tots.topping < t.topping
)
select toppings, tot_wt from tots;

#1# 5
#2# 3
#3# 2
#4# 4
#5# 1
#2#3# 5
#2#5# 4
#3#5# 3
#4#5# 5

If you want just those that are exactly 5kg, add this to the final where clause!

and you rated our response

   (1 rating)

Is this answer out of date? If it is, please let us know via a Review

Reviews

Thank you :)   
August 11, 2016 - 3:44 pm UTC
Reviewer: Reyes from Bolivia

https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:9531157800346395886 4/5

Anda mungkin juga menyukai