İlk düşüncem
select
<best solution>
from
<all possible combinations>
"En iyi çözüm" kısmı soruda tanımlanmıştır - en çok yüklü ve en az yüklü kamyonlar arasındaki en küçük fark. Diğer bit - tüm kombinasyonlar - düşünceyi duraklatmama neden oldu.
Üç A, B ve C siparişimiz ve üç kamyonumuz olduğu bir durumu düşünün. Olanaklar
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.
Bunların çoğu simetriktir. Örneğin, ilk altı sıra yalnızca her siparişin hangi kamyonda verildiği konusunda farklılık gösterir. Kamyonlar çalıştırılabilir olduğundan, bu düzenlemeler aynı sonucu verecektir. Şimdilik bunu görmezden geleceğim.
Permütasyonlar ve kombinasyonlar üretmek için bilinen sorgular vardır. Ancak, bunlar tek bir kova içinde düzenlemeler üretecektir. Bu sorun için birden fazla kova üzerinde düzenlemelere ihtiyacım var.
Çıktıya standart "tüm kombinasyonlar" sorgusundan bakma
;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.
Ben Sonuçlar her dikkate ait congnitive sıçrama yaparak Tablo A'da aynı model oluşturduk kaydetti sütun bir Sipariş olmak 1 , değerler Sipariş ve bu tutacaktır hangi kamyon söylemek satır kamyonların içinde Siparişlerin bir düzenleme olması. Sorgu daha sonra
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.
Bunu örnek verilerdeki on dört Siparişi kapsayacak şekilde genişleterek ve adları basitleştirdiğimizde:
;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.
Kolaylık sağlamak için ara sonuçları geçici tablolarda tutmayı tercih ederim.
Veriler önce UNPIVOTED ise sonraki adımlar çok daha kolay olacaktır.
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.
Ağırlıklar Siparişler tablosuna katılarak tanıtılabilir.
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
Soru artık en çok yüklenen ve en az yüklenen kamyonlar arasında en küçük farkın bulunduğu düzenlemeler bulunarak cevaplanabilir.
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
Tartışma
Bununla ilgili çok fazla sorun var. İlk olarak bir kaba kuvvet algoritmasıdır. Çalışma tablolarındaki satır sayısı kamyon ve sipariş sayısında üsteldir. #Arrangements içindeki satır sayısı (kamyon sayısı) ^ (sipariş sayısı). Bu iyi ölçeklenmeyecek.
İkincisi, SQL sorgularının içinde gömülü Emir sayısı vardır. Bunun tek yolu, kendine özgü sorunları olan dinamik SQL kullanmaktır. Siparişlerin sayısı binlerdeyse, oluşturulan SQL'in çok uzun olduğu bir zaman gelebilir.
Üçüncüsü, düzenlemelerdeki fazlalıktır. Bu, ara tabloları şişirerek çalışma süresini büyük ölçüde artırır.
Dördüncü olarak, # Düzenlemelerdeki birçok satır bir veya daha fazla kamyonu boş bırakır. Bu muhtemelen en uygun yapılandırma olamaz. Oluşturulduktan sonra bu satırları filtrelemek kolay olurdu. Kodu daha basit ve odaklanmış tutmak için yapmamayı seçtim.
Yukarı tarafta, kuruluşunuz dolu helyum balonları göndermeye başlarsa, negatif ağırlıkları idare eder!
Düşünceler
#FilledTrucks'u doğrudan kamyonlar ve Siparişler listesinden doldurmanın bir yolu olsaydı, bu endişelerin en kötüsünün yönetilebilir olacağını düşünüyorum. Ne yazık ki, bu engelim tökezledi. Ümit ediyorum ki gelecekteki bazı katılımcılar beni kaçıranları temin edebilir.
1 Sipariş için tüm ürünlerin aynı kamyonda olması gerektiğini söylüyorsunuz. Bu, atama atomunun OrderDetail değil Order olduğu anlamına gelir. Bunları test verilerinizden bu şekilde oluşturdum:
select
OrderId,
Size = sum(OrderDetailSize)
into #Order
from #OrderDetail
group by OrderId;
Bununla birlikte, söz konusu öğeleri 'Sipariş' veya 'SiparişDetay' olarak etiketlememiz fark etmez, çözüm aynı kalır.