2011-01-07 5 views
2

Ich bin ein Zustandsdiagramm der Art zu schaffen, wobei die Daten in einer einfachen Selbst referenzierenden Tabelle gespeichert werden (JobPath)rekursive Abfrage auf einer selbst referentiellen Tabelle (nicht hierarchisch)

JobId - ParentJobId 

I war ein Standard-SQL CTE Verwendung zu erhalten, die Daten aus dem funktioniert perfekt, bis ich mit den folgenden Daten

JobId - ParentId 
    1  2 
    2  3 
    3  4 
    4  2 

Jetzt endete, wie Sie Job 4 Links zu Job 2, die Arbeit geht 3 und dann auf Job 4 und so weiter sehen können.

Gibt es eine Möglichkeit, kann ich meine Abfrage sagen, Daten nicht zu ziehen, die es bereits hat?


Hier ist meine aktuelle Abfrage

WITH JobPathTemp (JobId, ParentId, Level) 
AS 
(
-- Anchor member definition 
    SELECT j.JobId, jp.ParentJobId, 1 AS Level 
    FROM Job AS j 
    LEFT OUTER JOIN dbo.JobPath AS jp 
     ON j.JobId = jp.JobId 
    where j.JobId=1516 
    UNION ALL 
-- Recursive member definition 
    SELECT j.JobId, jp.ParentJobId, Level + 1 
    FROM dbo.Job as j 
    INNER JOIN dbo.JobPath AS jp 
     ON j.JobId = jp.JobId 
    INNER JOIN JobPathTemp AS jpt 
     ON jpt.ParentId = jp.JobId 
     WHERE jp.ParentJobId <> jpt.JobId 
) 

- Erklärung, die den CTE

Server beitreten unterstützt keine UNION mit
SELECT * FROM JobPathTemp 
+0

Wie sieht Ihre Abfrage aus? Kannst du Distinct nicht benutzen? – Kell

+0

"DISTINCT-Operator ist im rekursiven Teil eines rekursiven allgemeinen Tabellenausdrucks 'JobPathTemp' nicht erlaubt." –

Antwort

3

wenn Sie Da es sich nicht um eine große Anzahl von Einträgen handelt, könnte die folgende Lösung geeignet sein. Die Idee besteht darin, den vollständigen "ID-Pfad" für jede Zeile zu erstellen und sicherzustellen, dass die "aktuelle ID" (im rekursiven Teil) noch nicht im verarbeiteten Pfad ist:

(Ich habe den Join zum Jobpfad zum Testen entfernt Zweck aber das Grundmuster sollte das gleiche sein)

 
WITH JobPathTemp (JobId, ParentId, Level, id_path) 
AS 
(
    SELECT jobid, 
     parentid, 
     1 as level, 
     '|' + cast(jobid as varchar(max)) as id_path 
    FROM job 
    WHERE jobid = 1 

    UNION ALL 

    SELECT j.JobId, 
      j.parentid, 
      Level + 1, 
      jpt.id_path + '|' + cast(j.jobid as varchar(max)) 
    FROM Job as j 
    INNER JOIN JobPathTemp AS jpt ON j.jobid = jpt.parentid 
            AND charindex('|' + cast(j.jobid as varchar), jpt.id_path) = 0 
) 
SELECT * 
FROM JobPathTemp 
; 
+0

Es gibt eine Grenze für die Datenmenge, die es gibt, also sollte diese Lösung 'funktionieren'. Ich bekomme viele Duplikate, die DISTINCT nicht entfernt, obwohl das seltsam ist. Aber mein C# -Code kann das ohne großen Aufwand erledigen. –

+0

Habe gerade gesehen, dass mein DISTINCT-Problem nicht zusammenhängt ... das funktioniert perfekt :) –

0

Diese Lösung funktioniert nicht, SQL ausführt zusammen der rekursive Ausdruck. Da Sie nicht auf die der Rekursion mit Ausnahme der Verbindung beziehen, TBH sehe ich keine Alternative eine gespeicherte Funktion zu verwenden ...


Sie nicht Ihre Frage schreiben haben ... aber ich versucht (in postgres, die in der gleichen Weise funktioniert) und wenn Sie „UNION“ (nicht „UNION ALL“) in der rekursiven Begriff verwenden, entfernen Sie sollten dann automatisch doppelte Zeilen:

with /*recursive*/ jobs as 
(select jobpath.jobid, jobpath.parentjobid from jobpath where jobid = 1 
    union 
    select jobpath.jobid, jobpath.parentjobid 
    from jobpath 
     join jobs on jobs.parentjobid = jobpath.jobid 
) 
select jobpath.* from jobpath join jobs on jobpath.jobid = jobs.jobid; 
+0

Ich habe meine Abfrage jetzt hinzugefügt, wenn ich nur zu Union ändere, bekomme ich diese "Rekursiver allgemeiner Tabellenausdruck 'JobPathTemp' enthält keinen UNION ALL-Operator auf oberster Ebene." –

+0

Das ist kein gutes Zeichen. Und Sie können sich nicht zweimal auf den rekursiven Ausdruck beziehen, z. eine WHERE NOT EXISTS-Klausel verwenden. – araqnid

+0

Nein, ich habe das auch versucht, aber es gibt einen Fehler, wenn ich zweimal auf JobPathTemp verweise (auch mit einem Alias) –