Suy nghĩ đầu tiên của tôi là
select
<best solution>
from
<all possible combinations>
Phần "giải pháp tốt nhất" được xác định trong câu hỏi - sự khác biệt nhỏ nhất giữa các xe tải được tải nhiều nhất và ít tải nhất. Một chút khác - tất cả các kết hợp - khiến tôi dừng lại để suy nghĩ.
Hãy xem xét một tình huống trong đó chúng tôi có ba đơn đặt hàng A, B và C và ba xe tải. Các khả năng là
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.
Nhiều trong số này là đối xứng. Sáu hàng đầu tiên, ví dụ, chỉ khác nhau ở chỗ mỗi chiếc xe tải được đặt. Vì các xe tải bị nấm nên các sắp xếp này sẽ tạo ra kết quả tương tự. Tôi sẽ bỏ qua điều này cho bây giờ.
Có các truy vấn đã biết để tạo ra hoán vị và kết hợp. Tuy nhiên, những thứ này sẽ tạo ra sự sắp xếp trong một thùng duy nhất. Đối với vấn đề này, tôi cần sắp xếp trên nhiều nhóm.
Nhìn vào đầu ra từ truy vấn "tất cả kết hợp" tiêu chuẩn
;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.
Tôi đã lưu ý các kết quả hình thành mô hình tương tự như Bảng A. Bằng cách thực hiện bước nhảy vọt xem xét mỗi cột là Đơn hàng 1 , các giá trị để nói xe tải nào sẽ giữ Đơn hàng đó và một hàng là sự sắp xếp Đơn hàng trong các xe tải. Các truy vấn sau đó trở thành
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.
Mở rộng điều này để bao gồm mười bốn Đơn đặt hàng trong dữ liệu mẫu và đơn giản hóa các tên chúng tôi nhận được:
;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.
Tôi chọn giữ các kết quả trung gian trong các bảng tạm thời cho thuận tiện.
Các bước tiếp theo sẽ dễ dàng hơn nhiều nếu dữ liệu được UNPIVOTED đầu tiên.
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.
Trọng lượng có thể được giới thiệu bằng cách tham gia vào bảng Đơn hàng.
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
Câu hỏi bây giờ có thể được trả lời bằng cách tìm (các) sự sắp xếp có sự khác biệt nhỏ nhất giữa các xe tải nhiều nhất và tải ít nhất
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
Thảo luận
Có rất nhiều vấn đề với điều này. Đầu tiên, nó là một thuật toán brute-force. Số lượng hàng trong các bàn làm việc là theo cấp số nhân của số lượng xe tải và đơn đặt hàng. Số lượng hàng trong #Arrangements là (số lượng xe tải) ^ (số lượng đơn đặt hàng). Điều này sẽ không quy mô tốt.
Thứ hai là các truy vấn SQL có số lượng Đơn hàng được nhúng trong chúng. Cách duy nhất để giải quyết vấn đề này là sử dụng SQL động, có vấn đề của riêng nó. Nếu số lượng đơn đặt hàng là hàng ngàn thì có thể đến một lúc khi SQL được tạo trở nên quá dài.
Thứ ba là sự dư thừa trong các sắp xếp. Điều này làm phồng các bảng trung gian tăng thời gian chạy rất nhiều.
Thứ tư, nhiều hàng trong #Arrangements để trống một hoặc nhiều xe tải. Đây có thể không phải là cấu hình tối ưu. Nó sẽ dễ dàng để lọc ra các hàng này khi tạo. Tôi đã chọn không làm như vậy để giữ cho mã đơn giản và tập trung hơn.
Về mặt này, xử lý các trọng số âm, doanh nghiệp của bạn có nên bắt đầu vận chuyển các balo helium đầy không!
Suy nghĩ
Nếu có một cách để nhập trực tiếp #FillsTrucks từ danh sách các xe tải và Đơn đặt hàng, tôi nghĩ rằng những mối quan tâm tồi tệ nhất sẽ có thể kiểm soát được. Đáng buồn thay, sự bất động của tôi vấp phải rào cản đó. Hy vọng của tôi là một số người đóng góp trong tương lai có thể cung cấp những gì đã lảng tránh tôi.
1 Bạn nói rằng tất cả các mặt hàng cho một đơn đặt hàng phải trên cùng một xe tải. Điều này có nghĩa là nguyên tử chuyển nhượng là Order, không phải OrderDetail. Tôi đã tạo ra những thứ này từ dữ liệu thử nghiệm của bạn, do đó:
select
OrderId,
Size = sum(OrderDetailSize)
into #Order
from #OrderDetail
group by OrderId;
Tuy nhiên, không có sự khác biệt nào cho dù chúng tôi dán nhãn các mục trong câu hỏi 'Đặt hàng' hoặc 'Đặt hàng', giải pháp vẫn giữ nguyên.