Stücklisten Auflistung MSSQL
Hallo!
Ich habe folgendes Problem:
Ich habe im Artikelstamm (Part$Product_2_1) die Artikel des ERP Systems.
Zu jedem Produktionsartikel gibt es eine Fertigungsliste (Part$Product_2_1_TAB) die aus Arbeitschritten (Operation$Operation_7_0) und wiederum aus Zukaufsteilen und weitern Fertigungsteilen Baugruppen) besteht. Die Baugruppen können wieder Unterbaugruppe und Zukaufsteile enthalten.
Die Fertigungsliste Part$Product_2_1_TAB ist mit dem Hauptartikel Part$Product_2_1 verknüpft und die Positionen der Fertigungliste wieder mit Part$Product_2_1 damit ich die Namen der Artikel der Positionen sehe.
Dies geht je nach Artikel über mehrere Stufen.
Meine momentane Lösung sieht so aus das ich über sechs joins die Teile zum Hauptartikel sehe. Jedoch immer in eigenen Spalten.
Mein Wunsch (Ziel) wäre den Hauptartikel und ALLE zugehörigen Artikel in einer Spalte aufzulisten.
Im Anhang Bild sieht man das mein Hauptartikel der 2.8705.051.5 ist dieser hat dann untern Anderem wieder die Baugruppe 2.8705.051.3 (Position1) der hätte unter Anderem wieder die Baugruppe 2.8705.053.3 (Postion 2) und darunter geht es immer so weiter. Postion 5 wäre in dem Fall NULL weil es nicht weiter nach "unten" geht.
Ich hätte eben gerne den Hauptartikel ausgewählt und dann darunter ALLE zugehörigen Teile angezeigt werden auch die der Baugruppen.
Hat jemand eine Idee wie ich das umsetzen könnte. Bei letzten Problem haben mir ukulele und Madmax sehr geholfen!
Ich hoffe ich konnte es halbwegs beschreiben.
Meine bisherige Abfrage:
Ich habe folgendes Problem:
Ich habe im Artikelstamm (Part$Product_2_1) die Artikel des ERP Systems.
Zu jedem Produktionsartikel gibt es eine Fertigungsliste (Part$Product_2_1_TAB) die aus Arbeitschritten (Operation$Operation_7_0) und wiederum aus Zukaufsteilen und weitern Fertigungsteilen Baugruppen) besteht. Die Baugruppen können wieder Unterbaugruppe und Zukaufsteile enthalten.
Die Fertigungsliste Part$Product_2_1_TAB ist mit dem Hauptartikel Part$Product_2_1 verknüpft und die Positionen der Fertigungliste wieder mit Part$Product_2_1 damit ich die Namen der Artikel der Positionen sehe.
Dies geht je nach Artikel über mehrere Stufen.
Meine momentane Lösung sieht so aus das ich über sechs joins die Teile zum Hauptartikel sehe. Jedoch immer in eigenen Spalten.
Mein Wunsch (Ziel) wäre den Hauptartikel und ALLE zugehörigen Artikel in einer Spalte aufzulisten.
Im Anhang Bild sieht man das mein Hauptartikel der 2.8705.051.5 ist dieser hat dann untern Anderem wieder die Baugruppe 2.8705.051.3 (Position1) der hätte unter Anderem wieder die Baugruppe 2.8705.053.3 (Postion 2) und darunter geht es immer so weiter. Postion 5 wäre in dem Fall NULL weil es nicht weiter nach "unten" geht.
Ich hätte eben gerne den Hauptartikel ausgewählt und dann darunter ALLE zugehörigen Teile angezeigt werden auch die der Baugruppen.
Hat jemand eine Idee wie ich das umsetzen könnte. Bei letzten Problem haben mir ukulele und Madmax sehr geholfen!
Ich hoffe ich konnte es halbwegs beschreiben.
Meine bisherige Abfrage:
select
ART.such AS Hauptartikel,
CASE WHEN ARTP.nummer <> '' THEN ARTP.nummer WHEN ARG.nummer <> '' THEN ARG.nummer END AS Position1_Id,
CASE WHEN ARTP.such <> '' THEN ARTP.such WHEN ARG.such <> '' THEN ARG.such END AS Position1,
CASE WHEN ARTP.such <> '' THEN ARTP.bsart END AS Position1_Bsart,
CASE WHEN ARTP1.such <> '' THEN ARTP1.such WHEN ARG1.such <> '' THEN ARG1.such END AS Position2,
CASE WHEN ARTP2.such <> '' THEN ARTP2.such WHEN ARG2.such <> '' THEN ARG2.such END AS Position3,
CASE WHEN ARTP3.such <> '' THEN ARTP3.such WHEN ARG3.such <> '' THEN ARG3.such END AS Position4,
CASE WHEN ARTP4.such <> '' THEN ARTP4.such WHEN ARG4.such <> '' THEN ARG4.such END AS Position5,
CASE WHEN ARTP5.such <> '' THEN ARTP5.such WHEN ARG5.such <> '' THEN ARG5.such END AS Position6,
POS.PK_zid,
POS.FK_elex,
CAST(ART.PK_id AS CHAR(30))PK_id
from dbo.Part$Product_2_1 ART (NOLOCK)
left join Part$Product_2_1_TAB POS ON POS.FK_id = ART.PK_id
left join Part$Product_2_1 ARTP ON ARTP.PK_id = POS.FK_elex (elex ist die PositionsId der Fertigungsliste)
left join Operation$Operation_7_0 ARG ON ARG.PK_id = POS.FK_elex (Hier wird auf Arbeitsschritte gejoint)
left join Part$Product_2_1_TAB POS2 ON POS2.FK_id = ARTP.PK_id
left join Part$Product_2_1 ARTP1 ON ARTP1.PK_id = POS2.FK_elex
left join Operation$Operation_7_0 ARG1 ON ARG1.PK_id = POS2.FK_elex
left join Part$Product_2_1_TAB POS3 ON POS3.FK_id = ARTP1.PK_id
left join Part$Product_2_1 ARTP2 ON ARTP2.PK_id = POS3.FK_elex
left join Operation$Operation_7_0 ARG2 ON ARG2.PK_id = POS3.FK_elex
left join Part$Product_2_1_TAB POS4 ON POS4.FK_id = ARTP2.PK_id
left join Part$Product_2_1 ARTP3 ON ARTP3.PK_id = POS4.FK_elex
left join Operation$Operation_7_0 ARG3 ON ARG3.PK_id = POS4.FK_elex
left join Part$Product_2_1_TAB POS5 ON POS5.FK_id = ARTP3.PK_id
left join Part$Product_2_1 ARTP4 ON ARTP4.PK_id = POS5.FK_elex
left join Operation$Operation_7_0 ARG4 ON ARG4.PK_id = POS5.FK_elex
left join Part$Product_2_1_TAB POS6 ON POS6.FK_id = ARTP4.PK_id
left join Part$Product_2_1 ARTP5 ON ARTP5.PK_id = POS6.FK_elex
left join Operation$Operation_7_0 ARG5 ON ARG5.PK_id = POS6.FK_elex
GROUP BY ART.such,ART.name,ARTP.such,ARTP1.such,ARTP2.such,POS.PK_zid,POS.FK_elex,ART.PK_id,ARG.such,ARG1.such,ARG2.such,ARTP3.such,ARG3.such,ARTP4.such,ARG4.such,ARTP5.such,ARG5.such,ARTP.nummer,ARG.nummer,ARTP.bsart
order by POS.PK_zid;
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 441153
Url: https://administrator.de/contentid/441153
Ausgedruckt am: 25.11.2024 um 16:11 Uhr
21 Kommentare
Neuester Kommentar
Hallo zusammen,
hier wäre ein Ansatz:
Transpose Table
Ergänzend dazu würde ich das gesamte Select in eine Common Table Expression packen, dann werden die Felder übersichtlicher dargestellt.
Ich denke auch mit row_number könnte man eine Lösung bauen.
hier wäre ein Ansatz:
Transpose Table
Ergänzend dazu würde ich das gesamte Select in eine Common Table Expression packen, dann werden die Felder übersichtlicher dargestellt.
Ich denke auch mit row_number könnte man eine Lösung bauen.
Bitte kein PIVOT, da wird einem ja schumrig bei. Der richtige Weg kann nur sein CTE zu nutzen und dazu gibt es schon jede Menge Treffer allein zu Stücklisten:
https://social.msdn.microsoft.com/Forums/security/de-DE/4b1c36be-a832-46 ...
https://stackoverflow.com/questions/239275/how-do-i-create-a-recursive-q ...
https://blog.sqlauthority.com/2012/04/24/sql-server-introduction-to-hier ...
Und wenn man da wirklich tief einsteigen will gibt es bei MSSQL sogar einen eigenen Datentyp HierachrchyID und eine Suche danach findet sehr ausführliche Beschreibungen zum Thema, angefangen bei der klassischen Stückliste.
https://www.sqlshack.com/use-hierarchyid-sql-server/
Einen anderen sehr schönen Artikel finde ich leider nicht, aber das Prinzip ist gut. Häufig kommt natürlich die DB nicht von dir sondern von einer Anwendung, dann bleibt vermutlich nur CTE das adequat zu lösen.
https://social.msdn.microsoft.com/Forums/security/de-DE/4b1c36be-a832-46 ...
https://stackoverflow.com/questions/239275/how-do-i-create-a-recursive-q ...
https://blog.sqlauthority.com/2012/04/24/sql-server-introduction-to-hier ...
Und wenn man da wirklich tief einsteigen will gibt es bei MSSQL sogar einen eigenen Datentyp HierachrchyID und eine Suche danach findet sehr ausführliche Beschreibungen zum Thema, angefangen bei der klassischen Stückliste.
https://www.sqlshack.com/use-hierarchyid-sql-server/
Einen anderen sehr schönen Artikel finde ich leider nicht, aber das Prinzip ist gut. Häufig kommt natürlich die DB nicht von dir sondern von einer Anwendung, dann bleibt vermutlich nur CTE das adequat zu lösen.
Hier mal mein Versuch deine Struktur nachzubauen:
Das sind jetzt nur einige Spalten, da fehlt noch was aber das kann man alles ausschmücken. t1 bilde ich nur weil dort viele Joins anfallen, das könnte man eventuell auch weg lassen. Wenn man das aber direkt in t2 integriert gehen glaube ich gar keine LEFT JOINs, daher ist das übersichtlicher und eventuell auch der einzige Weg. Oder man joint die anderen Spalten, z.B. aus Operation$Operation_7_0, erst ganz am Ende.
In t2 wirds dann rekursiv. Alle Teile, die Bestandteil von der Position in t1 sind, werden angehängt. Es entsteht eine Liste aller Teile die in der Hauptposition sind oder in einem Teil der Hauptposition.
Das kann dann im äußeren Select z.B. aggregiert werden (da wird es vermutlich noch irgendwo eine Anzahl geben und eventuell sind Bestandteile identisch und werden mehrmals gebraucht.
Man kann das ganze sehr gut mit einem Pfad veranschaulichen und/oder eine Spalte wie die erste Artikelnummer durchschleifen (siehe Spalte Hauptartikel).
Das ist die klasische Stückliste, das was dein Bild zeigt wäre tatsächlich eher PIVOT. Aber die releationale DB arbeitet eher mit Zeilen für gleichartige Dinge und ist darauf ausgelegt. Damit ist es dann auch einfacher weiter zu rechnen etc.
WITH t1 AS (
SELECT ART.PK_id AS ART_PK_id,
ART.such AS ART_such,
POS.FK_elex AS POS_FK_elex,
ARG.nummer AS ARG_nummer
FROM dbo.Part$Product_2_1 ART
LEFT JOIN Part$Product_2_1_TAB POS
ON ART.PK_id = POS.FK_id
LEFT JOIN Operation$Operation_7_0 ARG
ON POS.FK_elex = ARG.PK_id
), t2 AS (
SELECT t1.ART_PK_id AS Hauptartikel,
t1.ART_PK_id,
t1.ART_such,
t1.POS_FK_elex,
t1.ARG_nummer,
cast(t1.ART_PK_id AS VARCHAR(MAX)) AS pfad
FROM t1
WHERE t1.ART_such = '2.8705.051.5'
UNION ALL
SELECT t2.Hauptartikel,
t1.ART_PK_id,
t1.ART_such,
t1.POS_FK_elex,
t1.ARG_nummer,
cast(t2.pfad + '\' + t1.ART_PK_id AS VARCHAR(MAX)) AS pfad
FROM t2
INNER JOIN t1
ON t2.POS_FK_elex = t1.ART_PK_id
)
SELECT *
FROM t2
In t2 wirds dann rekursiv. Alle Teile, die Bestandteil von der Position in t1 sind, werden angehängt. Es entsteht eine Liste aller Teile die in der Hauptposition sind oder in einem Teil der Hauptposition.
Das kann dann im äußeren Select z.B. aggregiert werden (da wird es vermutlich noch irgendwo eine Anzahl geben und eventuell sind Bestandteile identisch und werden mehrmals gebraucht.
Man kann das ganze sehr gut mit einem Pfad veranschaulichen und/oder eine Spalte wie die erste Artikelnummer durchschleifen (siehe Spalte Hauptartikel).
Das ist die klasische Stückliste, das was dein Bild zeigt wäre tatsächlich eher PIVOT. Aber die releationale DB arbeitet eher mit Zeilen für gleichartige Dinge und ist darauf ausgelegt. Damit ist es dann auch einfacher weiter zu rechnen etc.
Am besten testest du erstmal jeden Select nacheinander. Also
1)
2)
und dann Alles. Ich vermute das Problem tiritt erst mit dem gesammten Code auf, warscheinlich liegt es an der Spalte pfad. Probier mal
1)
WITH t1 AS (
SELECT ART.PK_id AS ART_PK_id,
ART.such AS ART_such,
POS.FK_elex AS POS_FK_elex,
ARG.nummer AS ARG_nummer
FROM dbo.Part$Product_2_1 ART
LEFT JOIN Part$Product_2_1_TAB POS
ON ART.PK_id = POS.FK_id
LEFT JOIN Operation$Operation_7_0 ARG
ON POS.FK_elex = ARG.PK_id
)
SELECT *
FROM t1
WITH t1 AS (
SELECT ART.PK_id AS ART_PK_id,
ART.such AS ART_such,
POS.FK_elex AS POS_FK_elex,
ARG.nummer AS ARG_nummer
FROM dbo.Part$Product_2_1 ART
LEFT JOIN Part$Product_2_1_TAB POS
ON ART.PK_id = POS.FK_id
LEFT JOIN Operation$Operation_7_0 ARG
ON POS.FK_elex = ARG.PK_id
), t2 AS (
SELECT t1.ART_PK_id AS Hauptartikel,
t1.ART_PK_id,
t1.ART_such,
t1.POS_FK_elex,
t1.ARG_nummer,
cast(t1.ART_PK_id AS VARCHAR(MAX)) AS pfad
FROM t1
WHERE t1.ART_such = '2.8705.051.5'
)
SELECT *
FROM t2
WITH t1 AS (
SELECT ART.PK_id AS ART_PK_id,
ART.such AS ART_such,
POS.FK_elex AS POS_FK_elex,
ARG.nummer AS ARG_nummer
FROM dbo.Part$Product_2_1 ART
LEFT JOIN Part$Product_2_1_TAB POS
ON ART.PK_id = POS.FK_id
LEFT JOIN Operation$Operation_7_0 ARG
ON POS.FK_elex = ARG.PK_id
), t2 AS (
SELECT t1.ART_PK_id AS Hauptartikel,
t1.ART_PK_id,
t1.ART_such,
t1.POS_FK_elex,
t1.ARG_nummer,
cast(t1.ART_PK_id AS VARCHAR(MAX)) AS pfad
FROM t1
WHERE t1.ART_such = '2.8705.051.5'
UNION ALL
SELECT t2.Hauptartikel,
t1.ART_PK_id,
t1.ART_such,
t1.POS_FK_elex,
t1.ARG_nummer,
cast(t2.pfad + '\' + cast(t1.ART_PK_id AS VARCHAR(MAX)) AS VARCHAR(MAX)) AS pfad
FROM t2
INNER JOIN t1
ON t2.POS_FK_elex = t1.ART_PK_id
)
SELECT *
FROM t2
Eine View wird immer ausgeführt wenn sie aufgerufen wird. Ich bin nicht sicher wie das genau bei einer materialized view läuft, ich glaube aber das es der falsche Ansatz ist. Die Fragen sind doch erstmal was genau dauert lange (lass dir mal die Ausführungspläne im SQL Studio mit anzeigen beim ausführen), kann man die Ausführung beschleunigen (z.B. mit einem Index auf POS.FK_id), macht es überhaupt Sinn das für alle Daten zu tun (WHERE-Filter eventuell auch schon in den ersten Select packen) und zu guter letzt ließe sich das natürlich in eine Art Auswertungstabelle des Nachts ablegen. Es kommt darauf an wofür die Daten gebraucht werden.
Ich würde behaupten alles ist machbar aber sieht man denn nicht an der Spalte "Hauptartikel" das die zusammen gehören? Du kannst natürlich jede beliebige Spalte durchschleifen oder mit CASE etc. deinen Bedürfnissen anpassen. Wenn jetzt z.B. Ebene 2 immer die Hauptbaugruppe darstellt und das weiter vererben soll (ähnlich wie Ebene 1 im Prinzip den Hauptartikel weiter vererbt), dann würde ich folgendes tun:
WITH t1 AS (
SELECT
CAST(ART.PK_id AS CHAR(30))ART_PK_id,
ART.such AS ART_such,
ART.bsart AS ART1_bsart,
POS.FK_elex AS POS_FK_elex,
ART.MANDANT_ID AS ART_MANDANT_ID,
CASE WHEN ART1.such <> '' THEN ART1.such WHEN ARG.such <> '' THEN ARG.such END AS Position
FROM dbo.Part$Product_2_1 ART
LEFT JOIN Part$Product_2_1_TAB POS
ON ART.PK_id = POS.FK_id
LEFT JOIN Operation$Operation_7_0 ARG
ON POS.FK_elex = ARG.PK_id
LEFT JOIN dbo.Part$Product_2_1 ART1 ON POS.FK_elex = ART1.PK_id
), t2 AS (
SELECT
1 AS Ebene,
t1.ART_PK_id AS Hauptartikel,
CAST(NULL AS CHAR(30)) AS Hauptbaugruppe,
CAST(t1.ART_PK_id AS CHAR(30))ART_PK_id,
t1.ART_such AS baugruppe,
t1.ART1_bsart,
CAST(t1.POS_FK_elex AS CHAR(30))POS_FK_elex,
t1.ART_MANDANT_ID,
t1.Position,
cast(t1.ART_PK_id AS VARCHAR(MAX)) AS pfad
FROM t1
WHERE t1.ART_MANDANT_ID = 3 and t1.ART_such ='2.2421.001.5'
UNION ALL
SELECT t2.Ebene + 1,
t2.Hauptartikel,
(CASE WHEN t2.Ebene + 1 = 2 THEN CAST(t1.ART_PK_id AS CHAR(30)) ELSE t2.Hauptbaugruppe END),
CAST(t1.ART_PK_id AS CHAR(30))ART_PK_id,
t1.ART_such,
t1.ART1_bsart,
CAST(t1.POS_FK_elex AS CHAR(30))POS_FK_elex,
t1.ART_MANDANT_ID,
t1.Position,
cast(t2.pfad + '\' + cast(t1.ART_PK_id AS VARCHAR(MAX)) AS VARCHAR(MAX)) AS pfad
FROM t2
INNER JOIN t1
ON t2.POS_FK_elex = t1.ART_PK_id
)
SELECT *
FROM t2;
Du könntest technisch ca. 32768 Rekursionen in einem "Durchgang" machen, dannach wirds tricky.
Wieviele Baugruppen Unterbaugruppen sind ist der DB dabei erstmal egal. Was eine Hauptbaugruppe und was eine Unterbaugruppe ist und was dann in welcher Spalte weiter vererbt wird (wie im Beispiel Hauptartikel und Hauptbaugruppe) das ist eine Gestaltungsfrage. Du kannst das im CASE auch von anderen Werten abhängig machen, z.B. mit
Wieviele Baugruppen Unterbaugruppen sind ist der DB dabei erstmal egal. Was eine Hauptbaugruppe und was eine Unterbaugruppe ist und was dann in welcher Spalte weiter vererbt wird (wie im Beispiel Hauptartikel und Hauptbaugruppe) das ist eine Gestaltungsfrage. Du kannst das im CASE auch von anderen Werten abhängig machen, z.B. mit
(CASE WHEN t1.ART1_bsart IN ( 'Hauptartikel,'Hauptbaugruppe','Unterbaugruppe' ) THEN CAST(t1.ART_PK_id AS CHAR(30)) ELSE t2.Hauptbaugruppe END),