2017-06-29 1 views
0

Ich habe eine gespeicherte Prozedur geschrieben, die ein Elternteil, gehen Sie durch die Stücklisten Datensätze für diesen Teil und konditionieren passender Kind-Datensätze (dh die Teile, die enthalten sind) in der Stückliste dieses Teils), aber nur Kind-Datensätze, die bestimmte Kriterien erfüllen (unter Verwendung unserer Namenskonvention, alles beginnend mit '.', 'E' oder 'ZG').Verwenden Sie eine Variable im Cursor SELECT-Anweisung

Ich habe den SP erstellt und es funktioniert wunderbar, aber nur, wenn ich eine [optional] Elternteilenummer übergeben. Ich muss diesen SP für alle Teile ausführen, also dachte ich, den SP innerhalb eines Cursors auszuführen und im nächsten Teil weiterzumachen, bis keine mehr zu bearbeiten sind. Ich kann nicht herausfinden, wie das geht, oder wenn es sogar möglich ist, weil in der SELECT-Anweisung des Cursors (in der ich den gespeicherten Prozedurcode verwende) ich eine Variable übergeben muss, damit es funktioniert. Hier

ist der Code für die gespeicherte Prozedur:

CREATE PROCEDURE dbo.usp_BuildBOMLit 
@item_no CHAR(8) = NULL 
AS 
BEGIN 
WITH CTE AS (
     SELECT DISTINCT 
      LTRIM(RTRIM(lvl1.item_no)) as item_no, LTRIM(RTRIM(lvl1.comp_item_no)) as comp_item_no, 
      CASE 
       WHEN LTRIM(RTRIM(lvl1.comp_item_no)) LIKE '.%' 
        THEN 
         LTRIM(RTRIM(lvl1.comp_item_no)) 
       WHEN LTRIM(RTRIM(lvl1.comp_item_no)) LIKE 'E%' 
        THEN 
         (SELECT TOP 1 LTRIM(RTRIM(comp_item_no)) FROM bmprdstr_sql WHERE LTRIM(RTRIM(item_no))=LTRIM(RTRIM(lvl1.comp_item_no)) AND LTRIM(RTRIM(comp_item_no)) LIKE '.%') 
       WHEN LTRIM(RTRIM(lvl1.comp_item_no)) LIKE 'ZG%' 
        THEN 
         (SELECT TOP 1 LTRIM(RTRIM(comp_item_no)) FROM bmprdstr_sql WHERE LTRIM(RTRIM(item_no))=LTRIM(RTRIM(lvl1.comp_item_no)) AND LTRIM(RTRIM(comp_item_no)) LIKE '.%') 
        ELSE 
         NULL 
      END as lvl_2_comp_item_no 
     FROM 
      bmprdstr_sql as lvl1 
      LEFT JOIN bmprdstr_sql lvl2 ON lvl1.comp_item_no=lvl2.item_no 
     WHERE 
      (lvl1.item_no = @item_no) 
      AND (lvl1.comp_item_no LIKE '.%' OR lvl1.comp_item_no LIKE 'ZG%' OR lvl1.comp_item_no LIKE 'E%') 
    ) 
    SELECT DISTINCT 
     CASE 
      WHEN LEFT(item_no,1)='.' 
       THEN STUFF(item_no,1,1,'') 
      ELSE 
       item_no 
     END as item_no, 
     part_no = 
      STUFF((SELECT DISTINCT ',' + 
        CASE 
         WHEN LEFT(lvl_2_comp_item_no,1)='.' 
          THEN STUFF(lvl_2_comp_item_no,1,1,'') 
         ELSE lvl_2_comp_item_no 
         END 
        FROM CTE where [email protected]_no FOR XML PATH('')),1,1,'') 
    FROM 
     CTE 
    WHERE 
     lvl_2_comp_item_no IS NOT NULL AND item_no IS NOT NULL 
END 

Ausgang ist genau das Format ich brauche:

item_no | part_no

JM9027 | GS10702, LB2391, LB2704, LB2834, LB2896, LB6996

Wenn ich die Cursor zu erstellen, ich bin mit dem gleichen Code in dem SELECT Anweisung des Cursors, aber wie Sie sehen können, wenn die Eltern erfordern (@itemno) in übergeben werden Ich habe es vergeblich versucht:

SET NOCOUNT ON; 
DECLARE @itemno CHAR(15); 
DECLARE @partno VARCHAR(254); 
DECLARE @outside_cursor AS CURSOR; 

SET @outside_cursor = CURSOR FAST_FORWARD FOR 
WITH CTE AS 
(SELECT DISTINCT 
     LTRIM(RTRIM(lvl1.item_no)) AS item_no, 
     LTRIM(RTRIM(lvl1.comp_item_no)) AS comp_item_no, 
     CASE 
      WHEN LTRIM(RTRIM(lvl1.comp_item_no)) LIKE '.%' 
       THEN LTRIM(RTRIM(lvl1.comp_item_no)) 
      WHEN LTRIM(RTRIM(lvl1.comp_item_no)) LIKE 'E%' 
       THEN 
        (SELECT TOP 1 
         LTRIM(RTRIM(comp_item_no)) 
        FROM 
         bmprdstr_sql 
        WHERE 

LTRIM(RTRIM(item_no))=LTRIM(RTRIM(lvl1.comp_item_no)) 
         AND LTRIM(RTRIM(comp_item_no)) LIKE '.%' 
        ) 
      WHEN LTRIM(RTRIM(lvl1.comp_item_no)) LIKE 'ZG%' 
       THEN 
        (SELECT TOP 1 
         LTRIM(RTRIM(comp_item_no)) 
        FROM 
         bmprdstr_sql 
        WHERE 

LTRIM(RTRIM(item_no))=LTRIM(RTRIM(lvl1.comp_item_no)) 
         AND LTRIM(RTRIM(comp_item_no)) LIKE '.%' 
        ) 
      ELSE NULL 
     END AS lvl_2_comp_item_no 
FROM 
    bmprdstr_sql AS lvl1 
    LEFT JOIN bmprdstr_sql lvl2 ON lvl1.comp_item_no=lvl2.item_no 
WHERE 
    (lvl1.item_no = @itemno) -- <-- problem 
    AND (lvl1.comp_item_no LIKE '.%' OR lvl1.comp_item_no LIKE 'ZG%' 
OR lvl1.comp_item_no LIKE 'E%') 
) 
SELECT DISTINCT 
    CASE 
     WHEN LEFT(item_no,1)='.' 
      THEN STUFF(item_no,1,1,'') 
     ELSE item_no 
    END AS item_no, 
    part_no = 
     STUFF(
      (SELECT DISTINCT ',' + 
       CASE 
        WHEN LEFT(lvl_2_comp_item_no,1)='.' 
         THEN STUFF(lvl_2_comp_item_no,1,1,'') 
        ELSE lvl_2_comp_item_no 
       END 
      FROM 
       CTE 
      WHERE 
       [email protected] FOR XML PATH('')),1,1,'') 
        --^problem 
FROM 
    CTE 
WHERE 
    lvl_2_comp_item_no IS NOT NULL 
    AND item_no IS NOT NULL 

OPEN @outside_cursor; 
FETCH NEXT FROM @outside_cursor INTO @itemno, @partno; 

WHILE @@FETCH_STATUS = 0 
    BEGIN 
     INSERT INTO items_parts (item_no, part_no) 
     VALUES (@itemno, @partno) 
    FETCH NEXT FROM @outside_cursor INTO @itemno, @partno 
    END 
CLOSE @outside_cursor 
DEALLOCATE @outside_cursor 

Irgendwelche Ratschläge, wie dies erreicht werden kann?

+1

Nicht sicher, warum Sie einen Cursor zum Einfügen von Daten verwenden. Ich habe nicht versucht, diese große Abfrage zu analysieren, um wirklich zu sehen, was es tut, aber das kann als eine einzelne Menge aufgeschrieben werden. Hier ist ein großartiger Ort, um anzufangen. http://spaghettidba.com/2015/04/24/how-to-post-at-sql-question-on-a-public-forum/ –

+0

@SeanLange Jede Frage über SQL-Cursor endet mit diesem Kommentar in einer Form oder ein anderes. Ich habe versucht, einen Set-basierten Ansatz zu verwenden, bevor ich zum Cursor ging. – SpaceAge

+0

Das liegt daran, dass fast jeder Cursor mit einem Set-basierten Ansatz anstelle von RBAR gelöst werden kann. –

Antwort

0

Unabhängig davon, ob Sie sogar einen Cursor benötigen und Ihre Frage als akademisch behandeln, verwenden Sie den Cursor falsch.

Sie haben einen Proc, der tut, was Sie wollen, aber nur für einen Elternteil. Die Methode zum Einbinden eines Cursors besteht darin, den Cursor zu verwenden, um die Eltern zu durchlaufen und einfach die Variable item_no mit einem neuen item_no bei jeder Schleifenbildung zu füllen und dann den vorhandenen Code innerhalb des Cursors auszuführen.

Sie müssen nicht die SELECT und dann die INSERT in zwei separaten Abfragen tun, können Sie eine einzelne INSERT..SELECT in Ihrem Cursor. Der Cursor durchläuft nur die Liste der item_no-Werte.

+0

Wenn Sie sagen, ich brauche nicht die SELECT und INSERT in separaten Abfragen, sagen Sie, dass ich die INSERT ... SELECT in der Cursordefinition durchführen könnte? Wie 'SET @ outside_cursor = CURSOR SCHNELLER FORWARD FÜR INSERT INTO items_parts WERTE @itemno, @partno MIT CTE AS (SELECT ....)', dann in der WHILE-Schleife rufen Sie einfach 'FETCH NEXT'? – SpaceAge

+0

Nein, die Cursordefinition ist nur ein SELECT der 'item_no's, die Sie als' @ itemno' Variable verwenden möchten. INSERT..SELECT ist die Operation, die Sie in die Cursor-Schleife einfügen, so dass sie für jedes 'item_no' ausgeführt wird. –

+0

Schätzen Sie Ihre Hilfe @Tab Alleman. Ich konnte dies ohne einen Cursor erreichen. – SpaceAge

0

Ich sehe;

Ich denke, was Sie haben, ist ein CTE, die eine einzige Reihe von Daten zurückgibt (oder vielleicht (n) Reihen von Daten, sondern nur einzigartig für die @itemno)

CTE(item_no, comp_item_no, lvl_2_comp_item_no) 
     AS (
      --.... and the row will correspond to the @itemno provided 

aber was Sie stattdessen wollen, ist CTE das gibt den Wert für alle möglich [ietm_no] (etwas, das dann in einer temporären Tabelle hinter den Kulissen mit 3 Spalten [item_no], [comp_item_no], [lvl_2_comp_item_no] mit (n) Zeilen.

führen soll Sie können Ihren CURSOR verwenden, um diese nacheinander zu durchlaufen und in die CURSOR VARIABLES zu laden, zB:

FETCH NEXT FROM @outside_cursor INTO @itemno, @partno... 

und bearbeiten Sie die Einfügung für jede Zeile mit eindeutigen @ itemno.

BTW, ich habe bemerkt, dass Sie Ihren Cursor als @outside_cursor benannt haben, was bedeutet, dass es irgendwo auch einen @inside_cursor geben könnte? Falls Sie Cursor verschachteln (was nicht die beste Wahl ist), beachten Sie bitte, dass @@ FETCH_STATUS eine globale Variable ist und Sie den Wert in dieser Variablen getrennt für jeden Cursor verwalten müssen.

Sehe ich das richtig?

+0

Das Ergebnis des CTE ist das übergeordnete Element item_no wiederholt in der Spalte item_no, das comp_item_no ist die erste untergeordnete Ebene unterhalb des übergeordneten Elements und wenn der Wert comp_item_no mit ".", "E" oder "ZG" beginnt, wird es ausgeführt eine Unterabfrage und findet alle untergeordneten Elemente, die denselben Kriterien entsprechen. Die folgende Abfrage entfernt dann die '.' von der Vorderseite der item_no, wenn es eine gibt, und verkettet alle lvl_2_comp_item_no's in eine kommagetrennte Liste. Auch der Cursor heißt '@ outside_cursor', weil ich versucht habe, dies zu lösen, indem ich einen verschachtelten Cursor erstellt habe und ihn nie umbenannt habe. – SpaceAge

+0

ok, das ist gut, dass der Cursor nicht verschachtelt ist und wie wäre es mit meinem Vorschlag oben? Es scheint, dass Sie den Cursor nicht richtig verwenden. Ich denke, Sie müssen den Datensatz für alle Artikel laden und dann eins nach dem anderen verarbeiten ... macht Sinn? – Milan

+0

Was Sie beschreiben, ist letztendlich, was ich will, aber ich kann die korrekte Syntax nicht herausfinden, die verwendet wird, um die Daten zurückzugeben, ohne eine Variable im CTE zu verwenden. Ich werde versuchen, was Sie vorschlagen, und führen Sie die offene SP aus, um alle Daten in eine temporäre Tabelle einzufügen, bevor Sie den Cursor definieren und ausführen, um über die Zeilen zu iterieren. Ich melde mich zurück - danke für die Vorschläge. – SpaceAge

0

Ich schätze die Hilfe mit diesem Problem. Ich ging zurück zum Zeichenbrett und konnte mit der Stored Procedure und einer Self-Joining-Abfrage erreichen, was ich brauchte - kein Cursor nötig.

Verwandte Themen