2017-12-28 45 views
2

Kürzlich hatte ich die folgende Anfrage. Es gibt zwei Tabellen.komplexe Abfrage über die Kombination von Daten aus zwei Tabellen

-- lots of items 
declare @items table(id varchar(10), pieces integer) 
-- boxes 
declare @boxes table(num varchar(10), capacity integer) 



insert @items(id, pieces) 
select 'l1', 5 
union all select 'l2', 12 
union all select 'l3', 8 

insert @boxes(num, capacity) 
select 'o1',2 
union all select 'o2', 8 
union all select 'o3', 2 
union all select 'o4', 5 
union all select 'o5', 9 
union all select 'o6', 5 


-- list all pairs of items-boxes. So that item will be put in what order 
-- example: o1-l1, o2-l1, o2-l2, o3-l2, ... 

Bitte lassen Sie mich es zwingend notwendig, so erklären: Es gibt zwei Tabellen. Eins mit Gegenständen ein anderes mit Kästen. Wir müssen alle Artikel in Boxen auf die folgende Weise setzen:

Wir nehmen ersten Artikel l1 und ersten Kasten o1. Item l1 hat 5 Stück und Box o1 ist 2. Wir können maximal 2 Stück in die Box o1 legen. So erstellen wir die erste Zeile:

o1-l1 

Wir haben die Box o1 gefüllt. Umzug in die nächste Box o2. Es hat die Kapazität von 8 und wir haben Artikel l1, der noch 3 Stück hat. Setzt man die linke Stücke von l1 in die Box o2 und so schaffen wir den zweiten Datensatz:

o2-l1 

Wir alle Teile des Elements l1 in die Kästen gesetzt worden. Weiter zum nächsten Artikel l2. Es hat die 12 Stücke. Und wir haben noch 5 Kapazität in Box o2. Also wir setzen 5 Stück l2 in o2 und den nächsten Datensatz zu erstellen:

o2-l2 

Dann haben wir das nächste Feld, um einnehmen und die Schaffung der folgenden Datensatz:

o3-l2 

Und diese Art und Weise sind wir Erzeugen der Reihen bis wir alle Einzelteile in die Schachteln "legen". Die resultierende Abfrage sollte so etwas wie:

o1-l1 
o2-l1 
o2-l2 
o3-l2 
... 

Es in T-SQL mit dem Cursor und Sachen unbedingt gelöst werden kann, was nicht gut leistungsmäßig ist. Gibt es eine SQL-Abfrage, die die gewünschte Ausgabe generieren könnte?

+0

Hallo aussehen sollte. Welche Version von SQL Server verwenden Sie? –

+0

Hallo, SQL Server 2012 –

+0

Dies fällt unter die Kategorie der so genannten "Eimer füllen" Probleme. Es ist eigentlich relativ einfach, rekursives SQL zu lösen. Wenn ich später eine Chance bekomme (und die Frage ist noch nicht beantwortet), werde ich den Code entwerfen und posten. Aus Gründen der Übersichtlichkeit: Wie groß ist die Gesamtzahl der Elemente, die pro Abfrageausführung behandelt werden müssen? Tausende oder Zig Millionen? – Steve

Antwort

1

Ilya Sh, ich konnte nicht ganz überprüfen, ob Ihre eigene Antwort auf Ihre Frage richtig war. Aber hier ist mein Ansatz.

Ursprünglich dachte ich, dass dies mit einer rekursiven Abfrage gelöst werden könnte, aber das ist, wo alle Boxen gleich groß und garantiert größer als die Elemente sind, und die Elemente sind unteilbar, aber variieren in der Größe (so dass die einzige Entscheidung in Bezug auf jeden Artikel ist, ob es in der aktuellen Box verpackt werden kann, oder in der nächsten Box gehen muss).

In diesem Fall haben wir Gruppen von Elementen (dh eine Zeile, die einen Elementtyp und eine Menge angibt), in der die Elemente gleich groß sind, die Gruppen jedoch teilbar sind und jede Elementgruppe daher über eine Nummer verteilt werden kann von Boxen, und jede Box kann Teile einer Anzahl von Elementgruppen in einer Viele-zu-Viele-Beziehung zwischen Boxen und Elementgruppen enthalten.

Mein Denken ist, dass jede Box, je nach ihrer Kapazität, eine Reihe von einzelnen "Slots" (d. H. Ein Volumen von Raum), die einzelne Elemente erhalten kann.

Der Weg zu der Lösung besteht darin, eine "Nummerentabelle" zu verwenden, um die Mengen jeder Box/Artikelgruppe in einzelne Box-Slots und einzelne Artikel zu erweitern - eine Zeile pro Box-Slot und eine Zeile pro Artikel . Auf meinem Computer habe ich eine Tabelle namens zx_numbers - aber ich habe Code darunter eingefügt, der die Abhängigkeit von dieser Tabelle zu Illustrationszwecken beseitigt.

Sobald wir die Daten auf diese Weise normalisiert haben - indem wir Boxen in ihre einzelnen Slots expandieren und die Artikelgruppen und Summenmengen in einzelne Artikel expandieren - wird jeder Boxplatz und Artikel im gesamten Stapel fortlaufend nummeriert Zwei werden einfach auf dieser Sequenznummer zusammengefügt.

Ich habe eine FULL OUTER JOIN verwendet, um unübertroffene Slots/Elemente zu erhalten. Dies gibt uns eine sehr allgemeine und anpassungsfähige Lösung für das Problem, die wir dann auf verschiedene Weise weiterverarbeiten können, um die spezifischen Daten zu erhalten, die wir wollen (in diesem Fall nur eine Zusammenfassung der Kombinationen aus Box und Artikelgruppe).

Die Art und Weise, wie ich die Abfrage geschrieben habe, sind Boxen mit nicht gefülltem Speicherplatz (oder Artikelgruppen, die einen Rest hinterlassen, nachdem alle Felder vollständig ausgefüllt wurden) in den Ergebnissen und am Ende platziert, aber diese können gefiltert werden wenn nicht erforderlich.

WITH 
item_groups(item_group_id, group_qty) AS 
(
    select 'l1', 5 
    union all select 'l2', 12 
    union all select 'l3', 8 
    --union all select 'l4', 8 
) 

,boxes(box_id, capacity) AS 
(
    select 'o1',2 
    union all select 'o2', 8 
    union all select 'o3', 2 
    union all select 'o4', 5 
    union all select 'o5', 9 
    union all select 'o6', 5 
) 

,zx_numbers(zx_number) AS 
(
    --SELECT * FROM dbo.zx_numbers 

    --I have a dedicated numbers table on my machine, but I've substituted a 
    --manual sequence generator for the purposes of a self-contained demonstration 
    SELECT 
     (ones.n) + (10 * tens.n) + (100 * hundreds.n) AS zx_number 

    FROM --range 0 to 999 
     (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS ones(n) 
     ,(VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS tens(n) 
     ,(VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS hundreds(n) 
) 

,items AS 
(
    SELECT 
     item_groups.* 
     ,zx_number AS group_item_number 
     ,ROW_NUMBER() OVER (ORDER BY item_group_id, zx_number) AS batch_item_number 

    FROM 
     item_groups 

    INNER JOIN 
     zx_numbers 
     ON (zx_number BETWEEN 1 AND item_groups.group_qty) 
) 

,box_slots AS 
(
    SELECT 
     boxes.* 
     ,zx_number AS box_slot_number 
     ,ROW_NUMBER() OVER (ORDER BY box_id, zx_number) AS batch_slot_number 

    FROM 
     boxes 

    INNER JOIN 
     zx_numbers 
     ON (zx_number BETWEEN 1 AND boxes.capacity) 
) 

,box_item_matches AS 
(
    SELECT 
     COALESCE(bxsl.batch_slot_number, itms.batch_item_number) AS slot_number 

     ,bxsl.box_id 
     ,bxsl.capacity 
     ,bxsl.box_slot_number 

     ,itms.item_group_id 
     ,itms.group_qty 
     ,itms.group_item_number 

    FROM 
     box_slots AS bxsl 

    FULL OUTER JOIN 
     items AS itms 
     ON (bxsl.batch_slot_number = itms.batch_item_number) 
) 

--SELECT * FROM box_item_matches 

SELECT 
    box_id 
    ,item_group_id 

FROM 
    box_item_matches 

GROUP BY 
    box_id, item_group_id 

ORDER BY 
    IIF(box_id IS NULL OR item_group_id IS NULL, 1, 0) --i.e. NULLS LAST 
    ,box_id 
    ,item_group_id 
+0

Danke Steve, ich werde versuchen, es zu überprüfen, wenn ich zu meiner SQL-Umgebung komme. –

0

Danke für den Tipp von den Kommentaren der Abfrage so etwas wie

select f.id, b.num 
from (select z.*, 
     sum(z.qty) over (order by num) as cap 
    from @orders z 
) b join 
(select f.*, 
     sum(f.qty) over (order by id) as filler 
    from @lots f 
) f 
on f.filler - f.qty < b.cap and 
    f.filler > b.cap - b.qty 
order by f.id, b.num 
Verwandte Themen