2016-04-26 3 views
0

Ich habe einen rekursiven CTE, die mir eine Auflistung einer Reihe von Eltern-Kind-Schlüssel gibt wie folgt, seinen Namen in einer temporären Tabelle kann sagen, [#relationtree]:Wie können wir eine Kopie der Struktur rekursiv in eine andere Tabelle einfügen, wenn wir eine Eltern/Kind-Schlüsseltabelle verwenden?

Parent | Child 
-------------- 
1  | 3 
3  | 5 
5  | 6 
5  | 9 

Ich mag eine Kopie davon erstellen Beziehungen in eine Tabelle mit, sagen wir mal, die folgende stucture:

CREATE TABLE [dbo].[Relations] 
(
    [Id] int identity(1,1) 
    [ParentId] int 
) 

wie kann ich die oben genannten Aufzeichnungen aber rekursiv erhalten, um den zuvor eingefügten Identitätswert einsetzen, diesen Wert als ParentId Spalte einfügen zu können, für jede Kopie von ein Kind, das ich einfüge?

Ich würde erwarten, am Ende dieses haben in [dbo]. [Beziehungen] (angesichts unserer aktuellen Startwert ist, sagen 50)

Id | ParentId 
------------- 
... other rows present before this query ... 
50 | NULL 
51 | 50 
52 | 51 
53 | 51 

Ich bin nicht sicher, dass scope_identity arbeiten können Diese Situation oder das Erstellen einer neuen temporären Tabelle mit einer Liste neuer IDs und das manuelle Einfügen von Identitätsspalten ist der richtige Ansatz?

Ich könnte einen Cursor/eine Schleife schreiben, um dies zu tun, aber es muss eine gute Möglichkeit geben, einige rekursive Auswahl Magie zu tun!

+0

sorry, ich zweimal durch Ihre Frage ging, aber nicht in der Lage much..particularly dies herauszufinden Teil. "Wie kann ich die obigen Datensätze einfügen, aber rekursiv den zuvor eingefügten Identitätswert abrufen, um diesen Wert als die ParentId-Spalte für jede Kopie eines untergeordneten Elements einfügen zu können?" .Können Sie einige Beispieldaten der erwarteten Ausgabe mit einigen Beispiel-Eingabe – TheGameiswar

Antwort

1

Da Sie versuchen, den Baum in ein Segment der Tabelle zu bringen, sieht es so aus, als müssten Sie sowieso SET IDENTITY_INSERT ON für die Tabelle verwenden. Sie müssen sicherstellen, dass Platz für den neuen Baum ist. In diesem Fall nehme ich an, dass 49 das aktuelle Maximum id in Ihrer Tabelle ist, so dass wir uns nicht darum kümmern müssen, einen Baum zu überrennen, der später in der Tabelle ist.

Sie müssen in der Lage sein, die IDs des alten Baums dem neuen Baum zuzuordnen. Es sei denn es eine Regel ist rund um die IDs, sollte die genaue Zuordnung irrelevant sein, solange es richtig ist, so dass in diesem Fall würde ich nur so etwas tun:

SET IDENTITY_INSERT dbo.Relations ON 

;WITH CTE_MappedIDs AS 
(
    SELECT 
     old_id, 
     ROW_NUMBER() OVER(ORDER BY old_id) + 49 AS new_id 
    FROM 
    (
     SELECT DISTINCT parent AS old_id FROM #relationtree 
     UNION 
     SELECT DISTINCT child AS old_id FROM #relationtree 
    ) SQ 
) 
INSERT INTO dbo.Relations (Id, ParentId) 
SELECT 
    CID.new_id, 
    PID.new_id 
FROM 
    #relationtree RT 
INNER JOIN CTE_MappedIDs PID ON PID.old_id = RT.parent 
INNER JOIN CTE_MappedIDs CID ON CID.old_id = RT.parent 
-- We need to also add the root node 
UNION ALL 
SELECT 
    NID.new_id, 
    NULL 
FROM 
    #relationtree RT2 
INNER JOIN CTE_MappedIDs NID ON NID.old_id = RT2.parent 
WHERE 
    RT2.parent NOT IN (SELECT DISTINCT child FROM #relationtree) 

SET IDENTITY_INSERT dbo.Relations OFF 

Ich habe nicht getestet, dass, aber wenn es funktioniert nicht wie erwartet, dann wird es hoffentlich in die richtige Richtung zeigen.

+0

Prost liefern, werden Sie einen Blick darauf werfen. Ich wollte nicht, dass ich den Baum zwischen einige existierende Datensätze einfügen wollte, sondern einfach an das Ende der Tabelle anhängen (daher nicht die IDENTITY INSERT). –

+0

Diese Methode wird "IDENTITY_INSERT" erfordern, auch wenn sie aufgrund der Zuordnung am Ende der Tabelle steht.Sie könnten einfach die "49" im Code durch einen Wert ersetzen, den Sie erhalten, wenn Sie SELECT MAX (id) ... in der Tabelle in derselben Transaktion ausführen. –

+1

Danke für die Hinweise! Habe ein paar Verbesserungen vorgenommen und es an mein Szenario angepasst - die Mapping-Logik war der Schritt, den ich brauchte! –

0

Ich weiß, dass Sie bereits eine funktionierende Antwort haben, aber ich denke, Sie können dasselbe ein wenig einfacher erreichen (nicht, dass irgendetwas mit Tom Hs Antwort falsch ist), indem Sie die LAG-Funktion verwenden, um die vorherige Zeile zu inspizieren, Angenommen, Sie haben SQL Server 2012 oder höher.

Setup:

CREATE TABLE #relationtree (
    Parent INT, 
    Child INT 
) 

CREATE TABLE #relations (
    Id INT IDENTITY(1,1), 
    ParentId INT 
) 

INSERT INTO #relationtree (Parent, Child) VALUES(1,3), (3,5), (5,6), (5,9) 

INSERT INTO #relations (ParentId) values(1), (3), (5) 

Lösung:

DECLARE @offset INT = IDENT_CURRENT('#relations') 

;WITH relationtreeids AS (
    SELECT *, 
      ROW_NUMBER() OVER(ORDER BY Parent, Child) - 2 AS UnmodifiedParentId -- Simulate an identity field 
    FROM #relationtree 
) 
INSERT INTO #relations 
-- The LAG window function allows you to inspect the previous row 
SELECT CASE WHEN LAG(Parent) OVER(ORDER BY Parent) IS NULL 
       THEN NULL 
      WHEN LAG(Parent) OVER(ORDER BY Parent) = Parent 
       THEN UnmodifiedParentId + @offset ELSE UnmodifiedParentId + @offset + 1 
     END AS ParentId 
FROM relationtreeids 

Ausgang:

Id ParentId 
1 1 
2 3 
3 5 
4 NULL 
5 4 
6 5 
7 5 
Verwandte Themen