ความคิดแรกของฉันคือ 
select
    <best solution>
from
    <all possible combinations>
ส่วน "ทางออกที่ดีที่สุด" ถูกกำหนดไว้ในคำถาม - ความแตกต่างน้อยที่สุดระหว่างรถบรรทุกที่บรรทุกมากที่สุดและน้อยที่สุด อีกเล็กน้อย - การรวมกันทั้งหมด - ทำให้ฉันหยุดคิดชั่วคราว
พิจารณาสถานการณ์ที่เรามีคำสั่งซื้อสามรายการคือ A, B และ C และสามรถบรรทุก ความเป็นไปได้คือ
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.
หลายสิ่งเหล่านี้มีความสมมาตร ยกตัวอย่างเช่นหกแถวแรกจะแตกต่างกันเฉพาะการสั่งซื้อรถบรรทุกเท่านั้น เนื่องจากรถบรรทุกมีความสามารถในการเชื่อมกันได้ arrangemets เหล่านี้จะให้ผลลัพธ์ที่เหมือนกัน ฉันจะไม่สนใจสิ่งนี้ในตอนนี้
มีคำถามที่รู้จักกันสำหรับการผลิตพีชคณิตและชุดค่าผสม อย่างไรก็ตามสิ่งเหล่านี้จะจัดทำภายในถังเดียว สำหรับปัญหานี้ฉันต้องการการจัดการข้ามหลายถัง
ดูผลลัพธ์จากแบบสอบถาม "ชุดค่าผสมทั้งหมด" มาตรฐาน
;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. โดยการกระโดด congnitive ในการพิจารณาแต่ละคอลัมน์จะเป็นคำสั่งที่ 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.
ฉันเลือกที่จะเก็บผลลัพธ์ระดับกลางไว้ในตารางชั่วคราวเพื่อความสะดวก
ขั้นตอนต่อมาจะง่ายขึ้นมากถ้าข้อมูลไม่ได้รับการสนับสนุนครั้งแรก
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
อภิปรายผล
มีปัญหามากมายกับเรื่องนี้ ครั้งแรกมันเป็นอัลกอริทึมกำลังดุร้าย จำนวนแถวในตารางการทำงานเป็นเลขชี้กำลังเป็นจำนวนรถบรรทุกและคำสั่งซื้อ จำนวนแถวใน #Arangement คือ (จำนวนรถบรรทุก) ^ (จำนวนคำสั่งซื้อ) นี้จะไม่ขยายขนาดได้ดี
ประการที่สองคือแบบสอบถาม SQL มีจำนวนคำสั่งซื้อฝังอยู่ วิธีเดียวที่จะแก้ปัญหานี้คือการใช้ dynamic SQL ซึ่งมีปัญหาของตัวเอง หากจำนวนการสั่งซื้ออยู่ในหลักพันอาจถึงเวลาที่ SQL ที่สร้างขึ้นยาวเกินไป
ประการที่สามคือความซ้ำซ้อนในการเตรียมการ bloats นี้ตารางกลางเพิ่มขึ้นรันไทม์อย่างมหาศาล
ประการที่สี่แถวจำนวนมากใน #Arangements ปล่อยให้รถบรรทุกหนึ่งคันหรือมากกว่านั้นว่างเปล่า นี่อาจเป็นค่าที่ไม่เหมาะสม มันจะง่ายในการกรองแถวเหล่านี้เมื่อสร้าง ฉันเลือกที่จะไม่ทำเช่นนั้นเพื่อให้รหัสง่ายขึ้นและเน้น
องค์กรของคุณควรจัดการน้ำหนักเชิงลบหากองค์กรของคุณเริ่มจัดส่งฮีเลียมลูกโป่ง
ความคิด
หากมีวิธีการเติม #FilledTrucks โดยตรงจากรายการรถบรรทุกและคำสั่งซื้อฉันคิดว่าสิ่งที่เลวร้ายที่สุดของข้อกังวลเหล่านี้จะจัดการได้ การทำให้เศร้าใจของฉันสะดุดในอุปสรรค์นั้น ความหวังของฉันคือผู้มีส่วนร่วมในอนาคตบางคนอาจสามารถจัดหาสิ่งที่ทำให้ฉันหลุดพ้น
1คุณว่ารายการทั้งหมดสำหรับการสั่งซื้อจะต้องอยู่ในรถบรรทุกเดียวกัน ซึ่งหมายความว่าอะตอมของการมอบหมายคือคำสั่งไม่ใช่ OrderDetail ฉันได้สร้างสิ่งเหล่านี้จากข้อมูลการทดสอบของคุณดังนี้:
select
    OrderId,
    Size = sum(OrderDetailSize)
into #Order
from #OrderDetail
group by OrderId;
มันไม่ได้ทำให้แตกต่างกันแม้ว่าเราจะติดป้ายรายการในคำถาม 'สั่งซื้อ' หรือ 'สั่งซื้อปลีก' โซลูชันก็ยังคงเหมือนเดิม