Ich habe eine Datenstruktur, wo verschiedene Datenelemente miteinander verwandt werden können. Ein Datensatz kann der Vater vieler untergeordneter Datensätze sein, aber ein untergeordneter Datensatz kann nur einen übergeordneten Datensatz haben. Ich arbeite an einer Abfrage, um diese Datensatz "Familien" zu überprüfen, ob sie alle als veraltet markiert sind, so dass ich sie löschen kann.CTE-Abfrage vereinfachen
Um dies zu lösen, schrieb ich eine CTE, die am untersten Kind Datensätze beginnt, und arbeitet den Baum, die Aufnahme des Status, um mit einem Familienstatus auf der obersten Ebene zu beenden.
Ein Problem, auf das ich mit dieser Methode stieß war, wenn ein Datensatz mehrere Kinder hat, endete ich mit mehreren Datensätzen für die oberste Ebene Eltern. In einigen Fällen könnte ein untergeordneter Pfad für die Löschung geeignet sein, der andere jedoch nicht. Ich konnte keine Möglichkeit finden, dies im CTE zu identifizieren, und endete mit einer Unterabfrage, wo ich die Anzahl der Pfade, die gelöscht werden können, zähle und nicht, dann nur Datensätze mit> 1 'delete' Pfaden und 0 zurücksende 'nicht löschen' Pfade.
Hier ist mein Code, komplett mit Beispieldaten
create table #demand (id int, [status] varchar(10))
create table #relation (parent int, child int)
insert into #demand values (1, 'active')
insert into #demand values (2, 'obsolete')
insert into #demand values (3, 'obsolete')
insert into #demand values (4, 'obsolete')
insert into #demand values (5, 'active')
insert into #demand values (6, 'obsolete')
insert into #relation values(2, 3)
insert into #relation values(2, 4)
insert into #relation values(4, 5)
--the CTE splitFamily traverses the split family records from bottom up, checking that each record is Obsolete. If any record is not
;with
splitFamily as (
select
D.id,
DR.child,
case when D.[status] = 'obsolete' then 'Y' else 'N' end as CanDelete
from #demand D
left outer join #relation DR on D.id = DR.parent
where DR.child is null
union all
select
D.id,
DR.child,
case when D.[status] = 'obsolete' and splitFamily.CanDelete = 'Y' then 'Y' else 'N' end as CanDelete
from
splitFamily
join #relation DR on splitFamily.id = DR.child
join #demand D on DR.parent = D.id
)
select id from (
select parentLevel.id,
sum(case when parentLevel.CanDelete = 'Y' then 1 else 0 end) as "Y",
sum(case when parentLevel.CanDelete = 'N' then 1 else 0 end) as "N"
from splitFamily parentLevel
--The following join ensures we only return top level parent records to be deleted.
left join splitFamily children on parentLevel.id = children.child
where children.id is null
group by parentLevel.id
) as splits where Y > 0 and N = 0
drop table #demand
drop table #relation
Dieser Code funktioniert und gibt Rekord 6 als nur geeignet Datensatz löschen. Wenn Datensatz 5 in 'veraltet' geändert wird, enthält die Abfrage in den Ergebnissen korrekt 2.
Meine Frage ist, ob es eine sauberere, klarere Möglichkeit gibt, diese geteilten Pfade innerhalb des CTE zu identifizieren, um die zusätzliche Unterabfrage und das Zählen der Pfade zu vermeiden. Für mich verschleiert dies den Zweck des Codes und macht es wesentlich schwieriger, ihn aufrechtzuerhalten.
also wann können Sie einen Knoten löschen? Ist es, wenn es veraltet ist und keines seiner Kinder nicht veraltet ist, keines der Kinderkinder veraltet usw.? Ist das die Regel? – Cato
@AndrewDeighton richtig. Der Knoten auf der obersten Ebene und alle untergeordneten Knoten müssen sich in einem veralteten Status befinden. Dann möchte ich die ID des obersten Knotens zurückgeben. (Meine Löschprozedur wird zurückgesetzt, um die gesamte Familie innerhalb einer einzigen Transaktion zu löschen.) –
Wenn Sie also beim Löschen Ihrer Hierarchie einen Löschvorgang gefunden haben, möchten Sie den Vorgang stoppen, damit Sie diesen Knoten melden können. Wie ... an diesem Punkt interessieren Sie sich nicht für die Nachkommen, richtig? – JNevill