मेरा पहला विचार था
select
<best solution>
from
<all possible combinations>
"सबसे अच्छा समाधान" भाग को प्रश्न में परिभाषित किया गया है - सबसे भरी हुई और कम से कम भरी हुई ट्रकों के बीच सबसे छोटा अंतर। अन्य बिट - सभी संयोजन - मुझे विचार के लिए विराम देते हैं।
ऐसी स्थिति पर विचार करें जहां हमारे पास तीन ऑर्डर ए, बी और सी और तीन ट्रक हैं। संभावनाएं हैं
Truck 1 Truck 2 Truck 3
------- ------- -------
A B C
A C B
B A C
B C A
C A B
C B A
AB C -
AB - C
C AB -
- AB C
C - AB
- C AB
AC B -
AC - B
B AC -
- AC B
B - AC
- B AC
BC A -
BC - A
A BC -
- BC A
A - BC
- A BC
ABC - -
- ABC -
- - ABC
Table A: all permutations.
इनमें से कई सममित हैं। उदाहरण के लिए, पहली छह पंक्तियाँ, केवल उसी क्रम में भिन्न होती हैं, जिसमें प्रत्येक क्रम को ट्रक में रखा जाता है। चूँकि ट्रक फंगस वाले हैं, इन व्यवस्थाओं के परिणाम समान होंगे। मैं अभी के लिए इसे अनदेखा करूंगा।
क्रमपरिवर्तन और संयोजन के निर्माण के लिए ज्ञात प्रश्न हैं। हालांकि, ये एक ही बाल्टी के भीतर व्यवस्था का उत्पादन करेंगे। इस समस्या के लिए मुझे कई बाल्टियों की व्यवस्था की आवश्यकता है।
मानक "सभी संयोजनों" क्वेरी से आउटपुट को देखते हुए
;with Numbers as
(
select n = 1
union
select 2
union
select 3
)
select
a.n,
b.n,
c.n
from Numbers as a
cross join Numbers as b
cross join Numbers as c
order by 1, 2, 3;
n n n
--- --- ---
1 1 1
1 1 2
1 1 3
1 2 1
<snip>
3 2 3
3 3 1
3 3 2
3 3 3
Table B: cross join of three values.
मैंने कहा कि परिणाम तालिका ए के समान पैटर्न का गठन करते हैं। प्रत्येक कॉलम को ऑर्डर 1 मानने की अनुमानी छलांग लगाकर , मानों को कहने के लिए कि कौन सा ट्रक उस ऑर्डर को रखेगा, और ट्रकों के भीतर ऑर्डर की व्यवस्था करने के लिए एक पंक्ति । फिर क्वेरी बन जाती है
select
Arrangement = ROW_NUMBER() over(order by (select null)),
First_order_goes_in = a.TruckNumber,
Second_order_goes_in = b.TruckNumber,
Third_order_goes_in = c.TruckNumber
from Trucks a -- aka Numbers in Table B
cross join Trucks b
cross join Trucks c
Arrangement First_order_goes_in Second_order_goes_in Third_order_goes_in
----------- ------------------- -------------------- -------------------
1 1 1 1
2 1 1 2
3 1 1 3
4 1 2 1
<snip>
Query C: Orders in trucks.
उदाहरण के डेटा में चौदह आदेशों को शामिल करने के लिए इसका विस्तार करना, और हमारे द्वारा प्राप्त नामों को सरल बनाना:
;with Trucks as
(
select *
from (values (1), (2), (3)) as T(TruckNumber)
)
select
arrangement = ROW_NUMBER() over(order by (select null)),
First = a.TruckNumber,
Second = b.TruckNumber,
Third = c.TruckNumber,
Fourth = d.TruckNumber,
Fifth = e.TruckNumber,
Sixth = f.TruckNumber,
Seventh = g.TruckNumber,
Eigth = h.TruckNumber,
Ninth = i.TruckNumber,
Tenth = j.TruckNumber,
Eleventh = k.TruckNumber,
Twelth = l.TruckNumber,
Thirteenth = m.TruckNumber,
Fourteenth = n.TruckNumber
into #Arrangements
from Trucks a
cross join Trucks b
cross join Trucks c
cross join Trucks d
cross join Trucks e
cross join Trucks f
cross join Trucks g
cross join Trucks h
cross join Trucks i
cross join Trucks j
cross join Trucks k
cross join Trucks l
cross join Trucks m
cross join Trucks n;
Query D: Orders spread over trucks.
मैं सुविधा के लिए अस्थायी तालिकाओं में मध्यवर्ती परिणाम पकड़ना चुनता हूं।
यदि डेटा पहले UNPIVOTED है तो बाद के चरण बहुत आसान होंगे।
select
Arrangement,
TruckNumber,
ItemNumber = case NewColumn
when 'First' then 1
when 'Second' then 2
when 'Third' then 3
when 'Fourth' then 4
when 'Fifth' then 5
when 'Sixth' then 6
when 'Seventh' then 7
when 'Eigth' then 8
when 'Ninth' then 9
when 'Tenth' then 10
when 'Eleventh' then 11
when 'Twelth' then 12
when 'Thirteenth' then 13
when 'Fourteenth' then 14
else -1
end
into #FilledTrucks
from #Arrangements
unpivot
(
TruckNumber
for NewColumn IN
(
First,
Second,
Third,
Fourth,
Fifth,
Sixth,
Seventh,
Eigth,
Ninth,
Tenth,
Eleventh,
Twelth,
Thirteenth,
Fourteenth
)
) as q;
Query E: Filled trucks, unpivoted.
आदेश तालिका में शामिल होने से वज़न का परिचय दिया जा सकता है।
select
ft.arrangement,
ft.TruckNumber,
TruckWeight = sum(i.Size)
into #TruckWeights
from #FilledTrucks as ft
inner join #Order as i
on i.OrderId = ft.ItemNumber
group by
ft.arrangement,
ft.TruckNumber;
Query F: truck weights
इस सवाल का जवाब अब उस व्यवस्था (ओं) को ढूंढकर दिया जा सकता है, जिनमें सबसे अधिक लोड वाले और कम-लोडेड ट्रकों के बीच सबसे छोटा अंतर है
select
Arrangement,
LightestTruck = MIN(TruckWeight),
HeaviestTruck = MAX(TruckWeight),
Delta = MAX(TruckWeight) - MIN(TruckWeight)
from #TruckWeights
group by
arrangement
order by
4 ASC;
Query G: most balanced arrangements
विचार-विमर्श
इसके साथ बहुत सारी समस्याएं हैं। सबसे पहले यह एक जानवर-बल एल्गोरिथ्म है। ट्रकों और आदेशों की संख्या में काम करने वाली तालिकाओं में पंक्तियों की संख्या घातीय है। # व्यवस्थाओं में पंक्तियों की संख्या (ट्रकों की संख्या) ^ (आदेशों की संख्या) है। यह अच्छा पैमाना नहीं होगा।
दूसरा यह है कि एसक्यूएल प्रश्नों में उनके द्वारा अंतर्निहित आदेशों की संख्या होती है। इसका एकमात्र तरीका गतिशील एसक्यूएल का उपयोग करना है, जिसकी अपनी समस्याएं हैं। यदि आदेशों की संख्या हजारों में है, तो उत्पन्न SQL बहुत लंबा होने पर एक समय आ सकता है।
तीसरा है व्यवस्थाओं में अतिरेक। यह मध्यवर्ती तालिकाओं को क्रमिक रूप से बढ़ाता है।
चौथा, # व्यवस्था में कई पंक्तियाँ एक या एक से अधिक ट्रकों को खाली छोड़ देती हैं। यह संभवतः इष्टतम विन्यास नहीं हो सकता है। रचना पर इन पंक्तियों को फ़िल्टर करना आसान होगा। मैंने कोड को सरल और केंद्रित रखने के लिए ऐसा नहीं करने के लिए चुना है।
ऊपर की तरफ यह नकारात्मक भार को संभालता है, क्या आपके उद्यम को कभी भी भरे हुए हीलियम के बलबूते को शुरू करना चाहिए!
विचार
अगर ट्रकों और आदेशों की सूची से सीधे #FilledTrucks को आबाद करने का एक तरीका था, तो मुझे लगता है कि इन चिंताओं में से सबसे अधिक प्रबंधनीय होगा। अफसोस की बात यह है कि मेरी बाधा उस बाधा पर ठोकर खा गई। मेरी आशा है कि भविष्य में कुछ योगदानकर्ता उस आपूर्ति को करने में सक्षम हो सकते हैं जिसने मुझे हटा दिया है।
1 आप कहते हैं कि एक ऑर्डर के लिए सभी आइटम एक ही ट्रक पर होने चाहिए। इसका मतलब है कि असाइनमेंट का परमाणु ऑर्डर है, ऑर्डरडेटेल नहीं। मैंने आपके परीक्षण डेटा से इस प्रकार उत्पन्न किया है:
select
OrderId,
Size = sum(OrderDetailSize)
into #Order
from #OrderDetail
group by OrderId;
इससे कोई फर्क नहीं पड़ता, हालांकि, चाहे हम आइटम को प्रश्न 'ऑर्डर' या 'ऑर्डरडेटेल' में लेबल करते हैं, समाधान एक ही रहता है।