ความท้าทายในการค้นหา: การสร้างถังขนาดใหญ่ขึ้นอยู่กับการวัดที่ไม่นับจำนวนแถว


12

ฉันจะอธิบายปัญหาในแง่ของการโหลดรถบรรทุกจำนวนคงที่พร้อมคำสั่งเท่าที่จะทำได้

ปัจจัยการผลิต:

@TruckCount - the number of empty trucks to fill

ชุด:

OrderId, 
OrderDetailId, 
OrderDetailSize, 
TruckId (initially null)

Ordersประกอบด้วยหนึ่งหรือมากกว่าOrderDetailsนั้น

ความท้าทายที่นี่คือการกำหนดให้TruckIdกับแต่ละระเบียน

คำสั่งเดียวไม่สามารถแยกข้ามรถบรรทุกได้

รถบรรทุกควรจะเป็นอย่างเท่าเทียมกัน * sum(OrderDetailSize)โหลดเป็นไปได้ที่วัดโดย

* สม่ำเสมอ: เดลต้าที่เล็กที่สุดที่ทำได้ระหว่างรถบรรทุกที่มีน้ำหนักน้อยที่สุดและรถบรรทุกที่โหลดมากที่สุด ตามคำจำกัดความนี้ 1,2,3 จะกระจายอย่างเท่าเทียมกันมากกว่า 1,1,4 ถ้ามันช่วยคุณแกล้งทำเป็นอัลกอริทึมสถิติสร้างฮิสโตแกรมความสูงได้

ไม่มีการพิจารณาน้ำหนักบรรทุกสูงสุด เหล่านี้เป็นรถบรรทุกยืดหยุ่นวิเศษ อย่างไรก็ตามจำนวนรถบรรทุกได้รับการแก้ไขแล้ว

มีวิธีแก้ปัญหาที่ชัดเจนซึ่งซ้ำแล้วซ้ำอีก - โรบินกลมจัดสรรคำสั่งซื้อ

แต่มันสามารถทำได้ตามตรรกะที่ตั้งไว้?

ความสนใจหลักของฉันคือ SQL Server 2014 หรือใหม่กว่า แต่การตั้งค่าโซลูชันพื้นฐานสำหรับแพลตฟอร์มอื่น ๆ ก็น่าสนใจเช่นกัน

นี่ให้ความรู้สึกเหมือนดินแดน Itzik Ben-Gan :)

แอปพลิเคชันในโลกแห่งความเป็นจริงของฉันกำลังแจกจ่ายภาระการประมวลผลไปยังที่เก็บจำนวนหนึ่งเพื่อให้ตรงกับจำนวนของ CPU แบบโลจิคัล ดังนั้นแต่ละ bucket ไม่มีขนาดสูงสุด อัพเดตสถิติโดยเฉพาะ ฉันแค่คิดว่ามันสนุกกว่าที่จะสรุปปัญหาให้กับรถบรรทุกเพื่อกำหนดกรอบความท้าทาย

CREATE TABLE #OrderDetail (
OrderId int NOT NULL,
OrderDetailId int NOT NULL PRIMARY KEY,
OrderDetailSize tinyint NOT NULL,
TruckId tinyint NULL)

-- Sample Data

INSERT #OrderDetail (OrderId, OrderDetailId, OrderDetailSize)
VALUES
(1  ,100    ,75 ),
(2  ,101    ,5  ),
(2  ,102    ,5  ),
(2  ,103    ,5  ),
(2  ,104    ,5  ),
(2  ,105    ,5  ),
(3  ,106    ,100),
(4  ,107    ,1  ),
(5  ,108    ,11 ),
(6  ,109    ,21 ),
(7  ,110    ,49 ),
(8  ,111    ,25 ),
(8  ,112    ,25 ),
(9  ,113    ,40 ),
(10 ,114    ,49 ),
(11 ,115    ,10 ),
(11 ,116    ,10 ),
(12 ,117    ,15 ),
(13 ,118    ,18 ),
(14 ,119    ,26 )
--> YOUR SOLUTION HERE

-- After assigning Trucks, Measure delta between most and least loaded trucks.
-- Zero is perfect score, however the challenge is a set based solution that will scale, and produce good results, rather
-- than iterative solution that will produce perfect results by exploring every possibility.

SELECT max(TruckOrderDetailSize) - MIN(TruckOrderDetailSize) AS TruckMinMaxDelta
FROM 
(SELECT SUM(OrderDetailSize) AS TruckOrderDetailSize FROM #OrderDetail GROUP BY TruckId) AS Truck


DROP TABLE #OrderDetail

7
ลักษณะนี้จะเป็นคลาสสิกปัญหาถังบรรจุ
Dan Guzman

1
Hugo Kornelisก็ทำงานได้ดีเช่นกัน
Erik Darling

ค่า OrderDetailSize ทั้งหมดจะเท่ากันสำหรับ OrderId ที่กำหนดหรือว่าเป็นเพียงการเกิดร่วมในข้อมูลตัวอย่างของคุณ
youcantryreachingme

@ youcantryreachingme อ่าจุดที่ดี ... ไม่ได้เป็นเพียงการมีส่วนร่วมในข้อมูลตัวอย่าง
Paul Holmes

คำตอบ:


5

ความคิดแรกของฉันคือ

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;

มันไม่ได้ทำให้แตกต่างกันแม้ว่าเราจะติดป้ายรายการในคำถาม 'สั่งซื้อ' หรือ 'สั่งซื้อปลีก' โซลูชันก็ยังคงเหมือนเดิม


4

ดูความต้องการในโลกแห่งความเป็นจริงของคุณ (ซึ่งฉันคิดว่าเป็นความพยายามที่จะสร้างสมดุลภาระงานของคุณในชุดของซีพียู) ...

มีเหตุผลที่คุณต้องกำหนดกระบวนการล่วงหน้าให้กับถัง / cpus เฉพาะหรือไม่ [พยายามเข้าใจความต้องการที่แท้จริงของคุณ]

ตัวอย่างการอัปเดตสถิติคุณทราบได้อย่างไรว่าการดำเนินการเฉพาะจะใช้เวลานานเท่าใด จะเกิดอะไรขึ้นถ้าการดำเนินการที่กำหนดเข้าสู่ความล่าช้าที่ไม่คาดคิด (เช่นการกระจายตัวของตาราง / ดัชนีมากกว่าที่วางแผนไว้ / มากเกินไปผู้ใช้ที่ใช้เวลานาน txn จะบล็อกการดำเนินงาน 'การอัพเดตสถิติ')


เพื่อวัตถุประสงค์ในการทำ load load โดยทั่วไปฉันจะสร้างรายการงาน (เช่นรายการของตารางที่มีการอัพเดตสถิติ) และวางรายการกล่าวไว้ในตาราง (ชั่วคราว / เริ่มต้น)

โครงสร้างของตารางสามารถแก้ไขได้ตามความต้องการของคุณเช่น:

create table tasks
(id        int             -- auto-increment?

,target    varchar(1000)   -- 'schema.table' to have stats updated, or perhaps ...
,command   varchar(1000)   -- actual command to be run, eg, 'update stats schema.table ... <options>'

,priority  int             -- provide means of ordering operations, eg, maybe you know some tasks will run really long so you want to kick them off first
,thread    int             -- identifier for parent process?
,start     datetime        -- default to NULL
,end       datetime        -- default to NULL
)

ถัดไปฉันเริ่มการทำงานจำนวน X ของกระบวนการที่เกิดขึ้นพร้อมกันเพื่อดำเนินการ 'การอัพเดตสถิติ' จริงโดยแต่ละกระบวนการดำเนินการดังต่อไปนี้:

  • วางล็อคแบบเอกสิทธิ์เฉพาะบุคคลไว้บนtasksโต๊ะ (รับรองว่าไม่มีงานใดถูกหยิบมาได้มากกว่าหนึ่งกระบวนการ; ควรจะเป็นการล็อคช่วงสั้น ๆ )
  • ค้นหาแถว 'แรก' ที่start = NULL('แรก' จะถูกกำหนดโดยคุณเช่นสั่งโดยpriority?)
  • ชุดอัพเดตแถว start = getdate(), thread = <process_number>
  • กระทำการปรับปรุง (และปล่อยล็อคพิเศษ)
  • จดบันทึกidและtarget/commandค่าต่างๆ
  • ดำเนินการตามที่ต้องการกับtarget(สลับกันทำงานcommand) และเมื่อเสร็จแล้ว ...
  • อัปเดตtasksด้วยend = getdate() where id = <id>
  • ทำซ้ำข้างบนจนกว่าจะไม่มีงานที่ต้องทำอีกต่อไป

ด้วยการออกแบบด้านบนตอนนี้ฉันมีการทำงานที่สมดุล (ส่วนใหญ่) แบบไดนามิก

หมายเหตุ:

  • ฉันพยายามที่จะให้วิธีการจัดลำดับความสำคัญบางอย่างเพื่อที่ฉันจะได้เริ่มต้นงานที่ยาวขึ้นไปข้างหน้า ในขณะที่กระบวนการสองขั้นตอนกำลังทำงานกับงานที่ใช้เวลานานกว่ากระบวนการอื่น ๆ สามารถปั่นผ่านรายการงานที่สั้นลงได้
  • หากกระบวนการทำงานเข้าสู่การหน่วงเวลาที่ไม่ได้วางแผน (เช่นการรันนานการบล็อกผู้ใช้ txn) กระบวนการอื่น ๆ สามารถ 'เลือกการหย่อน' โดยการดึงการดำเนินการ 'ถัดไปที่มีอยู่' ออกจาก tasks
  • การออกแบบtasksตารางควรให้ประโยชน์อื่น ๆ เช่นประวัติเวลาทำงานที่คุณสามารถเก็บถาวรสำหรับการอ้างอิงในอนาคตประวัติเวลาทำงานที่สามารถใช้เพื่อแก้ไขลำดับความสำคัญให้สถานะของการดำเนินงานปัจจุบัน ฯลฯ
  • ในขณะที่ 'การล็อกแบบเอกสิทธิ์เฉพาะบุคคล' tasksอาจดูเหมือนมากเกินไปโปรดจำไว้ว่าเราต้องวางแผนสำหรับปัญหาที่อาจเกิดขึ้นของกระบวนการ 2 (หรือมากกว่า) ที่พยายามรับงานใหม่ในเวลาที่แน่นอนดังนั้นเราจึงต้องรับประกันงาน ถูกกำหนดให้กับกระบวนการเดียวเท่านั้น (และใช่คุณสามารถรับผลลัพธ์เดียวกันด้วยคำสั่ง 'update / select' คำสั่งผสม - ขึ้นอยู่กับความสามารถด้านภาษา SQL ของ RDBMS ของคุณ); ขั้นตอนของการได้รับ 'งาน' ใหม่ควรรวดเร็วเช่น 'ล็อคพิเศษ' ควรสั้นและในความเป็นจริงกระบวนการต่างๆจะถูกกดปุ่มtasksแบบสุ่มดังนั้นจะเป็นการบล็อกเล็กน้อย

โดยส่วนตัวแล้วฉันพบว่าtasksกระบวนการที่ขับเคลื่อนด้วยตารางนี้ง่ายต่อการติดตั้งและบำรุงรักษา ... ซึ่งตรงข้ามกับกระบวนการที่ซับซ้อนกว่าปกติในการพยายามกำหนดการแมปงาน / กระบวนการล่วงหน้า ... ymmv


เห็นได้ชัดว่าเป็นตัวอย่างที่ทำให้คุณเชื่อว่าคุณไม่สามารถให้รถบรรทุกของคุณกลับไปที่การกระจายสินค้า / คลังสินค้าสำหรับการสั่งซื้อต่อไปดังนั้นคุณต้องกำหนดคำสั่งซื้อล่วงหน้าให้กับรถบรรทุกต่างๆ (โปรดจำไว้ว่า UPS / Fedex / ฯลฯ กำหนดตามเส้นทางการจัดส่งเพื่อลดเวลาในการจัดส่งและการใช้ก๊าซ)

อย่างไรก็ตามในตัวอย่างในโลกแห่งความเป็นจริงของคุณ ('การอัพเดตสถิติ') ไม่มีเหตุผลว่าทำไมการมอบหมายงาน / กระบวนการไม่สามารถทำได้แบบไดนามิกดังนั้นจึงมั่นใจได้ว่ามีโอกาสที่ดีขึ้นในการสร้างสมดุลภาระงาน (ข้ามซีพียู .

หมายเหตุ: ฉันเห็นผู้คน (IT) พยายามที่จะมอบหมายงานของพวกเขาล่วงหน้า (เป็นรูปแบบของการโหลดบาลานซ์) ก่อนที่จะใช้งานจริง ๆ และในทุกกรณีที่เขาต้องปรับแต่งกระบวนการมอบหมายล่วงหน้าอย่างต่อเนื่อง ในการพิจารณาประเด็นงานที่เปลี่ยนแปลงอยู่ตลอดเวลา (เช่นระดับการแตกแฟรกเมนต์ในตาราง / ดัชนีกิจกรรมผู้ใช้พร้อมกัน ฯลฯ )


ประการแรกถ้าเราคิดว่า 'สั่งซื้อ' เป็นตารางและ 'สั่งซื้อรายละเอียด' เป็นสถิติที่เฉพาะเจาะจงบนโต๊ะแล้วเหตุผลที่ไม่แยกคือเพื่อหลีกเลี่ยงการล็อครอระหว่างถังการแข่งขัน Traceflag 7471 ออกแบบมาเพื่อกำจัดปัญหานี้ แต่ในการทดสอบของฉันฉันยังคงมีปัญหาการล็อค
Paul Holmes

แต่เดิมฉันหวังว่าจะแก้ปัญหาที่มีน้ำหนักเบามาก สร้างที่เก็บข้อมูลเป็นบล็อก SQL แบบหลายขั้นตอนเอกพจน์จากนั้น 'เริ่มต้นและลืม' แต่ละรายการโดยใช้งาน SQL Agent ที่ทำลายตัวเอง เช่นไม่มีการจัดการคิว อย่างไรก็ตามในภายหลังฉันพบว่าฉันไม่สามารถวัดปริมาณงานต่อสถิติได้อย่างง่ายดาย - จำนวนแถวไม่ได้ลดลง ไม่น่าแปลกใจจริงๆเนื่องจาก rowcount ไม่ได้แมปเชิงเส้นตรงกับจำนวน IO จากตารางหนึ่งหรือ stastic จริง ๆ ไปยังถัดไป ใช่แล้วสำหรับแอพพลิเคชั่นนี้มันสามารถสร้างความสมดุลให้ตัวเองได้อย่างแท้จริงด้วยการเพิ่มการจัดการคิวที่แอคทีฟตามที่คุณแนะนำ
Paul Holmes

ในการแสดงความคิดเห็นครั้งแรกของคุณ ... ใช่ยังคงมีการตัดสินใจ (ชัดเจน) เกี่ยวกับการออกคำสั่ง ... และปัญหาที่เกิดขึ้นพร้อมกันเช่น: คำสั่งบางอย่างสามารถทำงานในแบบคู่ขนานและได้รับประโยชน์จากการอ่านดิสก์ที่รวมกัน การจัดการคิวแบบไดนามิก (ค่อนข้างเบา) มีประสิทธิภาพมากกว่าการกำหนดถังไว้ล่วงหน้า :-) คุณมีชุดคำตอบ / แนวคิดที่ดีในการทำงานกับ ... ไม่ควรหนักเกินไปที่จะหาวิธีแก้ปัญหาที่ให้มา โหลดบาลานซ์ที่เหมาะสม
markp-fuso

1

สร้างและเติมตารางตัวเลขตามที่คุณต้องการนี่คือการสร้างครั้งเดียวเท่านั้น

 create table tblnumber(number int not null)

    insert into tblnumber (number)
    select ROW_NUMBER()over(order by a.number) from master..spt_values a
    , master..spt_values b

    CREATE unique clustered index CI_num on tblnumber(number)

สร้างตาราง Truck แล้ว

CREATE TABLE #PaulWhiteTruck (
Truckid int NOT NULL)

insert into #PaulWhiteTruck
values(113),(203),(303)

declare @PaulTruckCount int
Select @PaulTruckCount= count(*) from #PaulWhiteTruck

CREATE TABLE #OrderDetail (
id int identity(1,1),
OrderId int NOT NULL,
OrderDetailId int NOT NULL PRIMARY KEY,
OrderDetailSize int NOT NULL,
TruckId int NULL
)

INSERT
#OrderDetail (OrderId, OrderDetailId, OrderDetailSize)
VALUES
(
1 ,100 ,75 ),(2 ,101 ,5 ),
(2 ,102 ,5 ),(2 ,103 ,5 ),
(2 ,104 ,5 ),(2 ,105 ,5 ),
(3 ,106 ,100),(4 ,107 ,1 ),
(5 ,108 ,11 ),(6 ,109 ,21 ),
(7 ,110 ,49 ),(8 ,111 ,25 ),
(8 ,112 ,25 ),(9 ,113 ,40 ),
(10 ,114 ,49 ),(11 ,115 ,10 ),
(11 ,116 ,10 ),(12 ,117 ,15 ),
(13 ,118 ,18 ),(14 ,119 ,26 )

ฉันได้สร้างหนึ่งOrderSummaryตาราง

create table #orderSummary(id int identity(1,1),OrderId int ,TruckOrderSize int
,bit_value AS
CONVERT
(
integer,
POWER(2, id - 1)
)
PERSISTED UNIQUE CLUSTERED)
insert into #orderSummary
SELECT OrderId, SUM(OrderDetailSize) AS TruckOrderSize
FROM #OrderDetail GROUP BY OrderId

DECLARE @max integer =
POWER(2,
(
SELECT COUNT(*) FROM #orderSummary 
)
) - 1
declare @Delta int
select @Delta= max(TruckOrderSize)-min(TruckOrderSize)   from #orderSummary

กรุณาตรวจสอบค่า Delta ของฉันและแจ้งให้เราทราบหากมันผิด

;WITH cte 
     AS (SELECT n.number, 
                c.* 
         FROM   dbo.tblnumber AS N 
                CROSS apply (SELECT s.orderid, 
                                    s.truckordersize 
                             FROM   #ordersummary AS s 
                             WHERE  n.number & s.bit_value = s.bit_value) c 
         WHERE  N.number BETWEEN 1 AND @max), 
     cte1 
     AS (SELECT c.number, 
                Sum(truckordersize) SumSize 
         FROM   cte c 
         GROUP  BY c.number 
        --HAVING sum(TruckOrderSize) between(@Delta-25) and (@Delta+25) 
        ) 
SELECT c1.*, 
       c.orderid 
FROM   cte1 c1 
       INNER JOIN cte c 
               ON c1.number = c.number 
ORDER  BY sumsize 

DROP TABLE #orderdetail 

DROP TABLE #ordersummary 

DROP TABLE #paulwhitetruck 

คุณสามารถตรวจสอบผลของ CTE1 Permutation and Combination of order along with their sizeก็มีเป็นไปได้ทั้งหมด

หากวิธีการของฉันถูกต้องจนถึงที่นี่ฉันก็ต้องการความช่วยเหลือจากใครสักคน

งานที่ค้างอยู่:

ตัวกรองและแบ่งผลลัพธ์ของCTE1ใน 3 ส่วน ( Truck count) เช่นOrderidนี้ที่ไม่ซ้ำกันในแต่ละกลุ่มและแต่ละส่วน T ruckOrderSizeอยู่ใกล้กับเดลต้า


ตรวจสอบคำตอบล่าสุดของฉันฉันพลาดการค้นหาหนึ่งคำขณะโพสต์ไม่มีใครชี้ความผิดพลาดของฉันคัดลอกและเรียกใช้
KumarHarsh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.