내 첫 생각은
select
<best solution>
from
<all possible combinations>
"최고의 솔루션"부분은 가장 많이 적재 된 트럭과 가장 적게 적재 된 트럭 사이의 가장 작은 차이라는 질문에 정의되어 있습니다. 다른 비트-모든 조합-생각을 멈추게했습니다.
3 개의 주문 A, B 및 C와 3 대의 트럭이있는 상황을 고려하십시오. 가능성은
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.
이들 중 다수는 대칭입니다. 예를 들어, 처음 6 행은 각 주문이 수행되는 트럭 만 다릅니다. 트럭은 가독성이 있기 때문에 이러한 배치는 동일한 결과를 낳습니다. 지금은 이것을 무시하겠습니다.
순열 및 조합 생성에 대한 알려진 쿼리가 있습니다. 그러나 이것들은 단일 버킷 내에서 배열을 생성합니다. 이 문제를 해결하려면 여러 버킷에서 배열이 필요합니다.
표준 "모든 조합"쿼리의 결과보기
;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.
결과는 표 A와 동일한 패턴을 형성했습니다. 각 열 을 Order 1 로 간주하는인지적인 도약 , 어느 Order가 해당 Order를 보유 할 것인지를 나타내는 값 및 Truck 내의 Order를 배열 하는 행 을 나타냅니다. 그러면 쿼리가됩니다
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.
예제 데이터에서 14 개의 주문을 다루기 위해 이것을 확장하고 우리가 얻는 이름을 단순화하십시오.
;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
토론
이것에는 많은 문제가 있습니다. 먼저 무차별 알고리즘입니다. 작업 테이블의 행 수는 트럭 및 주문 수의 지수입니다. #Arrangements의 행 수는 (트럭 수) ^ (주문 수)입니다. 이것은 잘 확장되지 않습니다.
두 번째는 SQL 쿼리에 포함 된 주문 수가 있습니다. 이를 해결하는 유일한 방법은 자체 SQL에 문제가있는 동적 SQL을 사용하는 것입니다. 주문 수가 수천 개이면 생성 된 SQL이 너무 길어질 수 있습니다.
셋째, 협정의 중복성이다. 이로 인해 중간 테이블이 팽창하여 런타임이 크게 증가합니다.
넷째, #Arrangements의 많은 행은 하나 이상의 트럭을 비 웁니다. 최적의 구성이 아닐 수 있습니다. 작성시 이러한 행을 쉽게 필터링 할 수 있습니다. 코드를 단순하고 집중적으로 유지하기 위해 그렇게하지 않기로 선택했습니다.
기업이 채워진 헬륨 baloons를 선적하기 시작하면 위쪽에 이것은 부정적인 무게를 처리합니다!
생각
트럭 및 주문 목록에서 #FilledTrucks를 직접 채울 수있는 방법이 있다면 이러한 문제 중 최악의 상황은 관리가 가능할 것입니다. 슬프게도 저의 상상력은 그 장애물에 걸려 넘어졌습니다. 저의 희망은 미래의 공헌자가 나를 피할 수있는 것을 제공 할 수 있기를 바랍니다.
1 주문에 대한 모든 품목이 동일한 트럭에 있어야한다고 말합니다. 이는 할당 원자가 OrderDetail이 아니라 Order임을 의미합니다. 테스트 데이터에서 다음을 생성했습니다.
select
OrderId,
Size = sum(OrderDetailSize)
into #Order
from #OrderDetail
group by OrderId;
문제의 항목에 'Order'또는 'OrderDetail'이라는 레이블을 지정하더라도 솔루션은 동일하게 유지됩니다.