2013-04-28 10 views
6

Pracuję na bazie danych zasobów, który ma hierarchii. Istnieje również tabela "ReferenceAsset", która skutecznie wskazuje na zasób. Zasób referencyjny zasadniczo działa jako nadpisanie, ale jest wybierany tak, jakby był unikalnym, nowym zasobem. Jednym z nadpisań, które zostaną ustawione, jest parent_id.Serwer SQL: kwerenda danych hierarchicznych i odniesienia

Kolumny, które są istotne dla wyboru heirarchy:
Zaleta: id (pierwotne), parent_id
aktywa referencyjnego: Identyfikator (podstawowej), asset_id (foreignkey-> aktywami), parent_id (zawsze atutem)
- --EDITED 5/27 ----

próbki dowiemy się z tabeli danych (po dołącza)

id | asset_id | name   | parent_id | milestone | type 

    3  3  suit    null  march  shape 
    4  4  suit_banker   3   april  texture 
    5  5  tie    null  march  shape 
    6  6  tie_red    5   march  texture 
    7  7  tie_diamond   5   june  texture 
    -5  6  tie_red    4   march  texture 

id < 0 (jak ostatni wiersz) oznacza aktywa, do których się odwołuje. Zasoby z odniesieniami mają kilka kolumn, które są nadpisane (w tym przypadku ważny jest tylko identyfikator nadrzędny).

Oczekuje się, że jeśli wybiorę wszystkie aktywa od kwietnia, należy zrobić wtórny wybierz, aby uzyskać całe gałęzie drzewa zapytania Dopasowanie:

więc początkowo mecz zapytanie spowodowałoby:

4  4  suit_banker   3   april  texture 

Następnie po CTE, otrzymujemy pełną hierarchię, a nasz wynik powinien być to (jak dotąd to działa)

3  3  suit    null  march  shape 
    4  4  suit_banker   3   april  texture 
    -5  6  tie_red    4   march  texture 

i widać, rodzicem ID: -5 tam jest, ale czego brakuje, co jest potrzebne, jest określany aktywami, a dominującą odwołuje aktywów:

5  5  tie    null  march  shape 
    6  6  tie_red    5   march  texture 

Obecnie moje rozwiązanie działa w tym celu, ale ogranicza się tylko do jednej głębokości referencji (i uważam, że wdrożenie jest dość brzydkie).

--- Edytowany ---- Oto moja podstawowa funkcja wyboru. Powinno to lepiej pokazać, gdzie leży prawdziwa komplikacja: AssetReference.

Select A.id as id, A.id as asset_id, A.name,A.parent_id as parent_id, A.subPath, T.name as typeName, A2.name as parent_name, B.name as batchName, 
L.name as locationName,AO.owner_name as ownerName, T.id as typeID, 
M.name as milestoneName, A.deleted as bDeleted, 0 as reference, W.phase_name, W.status_name 
FROM Asset as A Inner Join Type as T on A.type_id = T.id 
Inner Join Batch as B on A.batch_id = B.id 
Left Join Location L on A.location_id = L.id 
Left Join Asset A2 on A.parent_id = A2.id 
Left Join AssetOwner AO on A.owner_id = AO.owner_id 
Left Join Milestone M on A.milestone_id = M.milestone_id 
Left Join Workflow as W on W.asset_id = A.id 
where A.deleted <= @showDeleted 

UNION 

Select -1*AR.id as id, AR.asset_id as asset_id, A.name, AR.parent_id as parent_id, A.subPath, T.name as typeName, A2.name as parent_name, B.name as batchName, 
L.name as locationName,AO.owner_name as ownerName, T.id as typeID, 
M.name as milestoneName, A.deleted as bDeleted, 1 as reference, NULL as phase_name, NULL as status_name 
FROM Asset as A Inner Join Type as T on A.type_id = T.id 
Inner Join Batch as B on A.batch_id = B.id 
Left Join Location L on A.location_id = L.id 
Left Join Asset A2 on AR.parent_id = A2.id 
Left Join AssetOwner AO on A.owner_id = AO.owner_id 
Left Join Milestone M on A.milestone_id = M.milestone_id 
Inner Join AssetReference AR on AR.asset_id = A.id 
where A.deleted <= @showDeleted 

Mam procedura składowana, która pobiera tabelę temp (#temp) i znajduje wszystkie elementy hierarchii.Strategia I zatrudnionych było to:

  1. Zaznacz cały heirarchy system w tabeli temp (#treeIDs) reprezentowane przez oddzielone przecinkami liście każdego całej gałęzi drzewa
  2. Pobierz całą heirarchy aktywów pasujących rekordów (od # temp)
  3. Uzyskaj wszystkie aktywa referencyjne wskazywanego przez aktywa z heirarchy
  4. Analizować heirarchy wszystkich aktywów referencyjnych

działa to na razie, ponieważ aktywa referencyjne są zawsze la element na gałęzi, ale jeśli nie, myślę, że będę miał kłopoty. Czuję, że potrzebuję lepszej formy rekurencji.

Tu jest mój bieżący kod, który działa, ale nie jestem z niego dumny, i wiem, że to nie jest mocny (ponieważ działa tylko wtedy, gdy występują odnośniki na dole):

Krok 1. zbudować całą hierarchię

;WITH Recursive_CTE AS (
SELECT Cast(id as varchar(100)) as Hierarchy, parent_id, id 
FROM #assetIDs 
Where parent_id is Null 

UNION ALL 

SELECT 
CAST(parent.Hierarchy + ',' + CAST(t.id as varchar(100)) as varchar(100)) as Hierarchy, t.parent_id, t.id 
FROM Recursive_CTE parent 
INNER JOIN #assetIDs t ON t.parent_id = parent.id 
) 



Select Distinct h.id, Hierarchy as idList into #treeIDs 
FROM (Select Hierarchy, id FROM Recursive_CTE) parent 
CROSS APPLY dbo.SplitIDs(Hierarchy) as h 

Krok 2. Wybierz gałęzie wszystkich aktywów pasujących zapytaniu

Select DISTINCT L.id into #RelativeIDs FROM #treeIDs 
CROSS APPLY dbo.SplitIDs(idList) as L 
WHERE #treeIDs.id in (Select id FROM #temp) 

Krok 3. Pobierz wszystkie aktywa referencyjne w oddziałach (aktywa referencyjne mają ujemne wartości id, stąd część id < 0)

Select asset_id INTO #REFLinks FROM #AllAssets WHERE id in 
(Select #AllAssets.asset_id FROM #AllAssets Inner Join #RelativeIDs 
on #AllAssets.id = #RelativeIDs.id Where #RelativeIDs.id < 0) 

Krok 4. Pierwsze oddziały cokolwiek znaleźć w kroku 3

Select DISTINCT L.id into #extraRelativeIDs FROM #treeIDs 
CROSS APPLY dbo.SplitIDs(idList) as L 
WHERE 
exists (Select #REFLinks.asset_id FROM #REFLinks WHERE #REFLinks.asset_id = #treeIDs.id) 
and Not Exists (select id FROM #RelativeIDs Where id = #treeIDs.id) 

Starałem się po prostu pokazać odpowiedni kod. Jestem bardzo wdzięczny każdemu, kto może mi pomóc znaleźć lepsze rozwiązanie!

+0

jakiej używasz wersji sql? http://msdn.microsoft.com/de-de/library/bb677290.aspx – NickD

+0

serwer sql 2012, ale przełączyliśmy się na niego, więc większość z tego została napisana na rok 2008 – haggercody

Odpowiedz

1
--getting all of the children of a root node (could be > 1) and it would require revising the query a bit 

DECLARE @AssetID int = (select AssetId from Asset where AssetID is null); 


--algorithm is relational recursion 
--gets the top level in hierarchy we want. The hierarchy column 
--will show the row's place in the hierarchy from this query only 
--not in the overall reality of the row's place in the table 

WITH Hierarchy(Asset_ID, AssetID, Levelcode, Asset_hierarchy) 
AS 
(
SELECT AssetID, Asset_ID, 
     1 as levelcode, CAST(Assetid as varchar(max)) as Asset_hierarchy 
FROM Asset 
WHERE [email protected] 

UNION ALL 

--joins back to the CTE to recursively retrieve the rows 
--note that treelevel is incremented on each iteration 

SELECT A.Parent_ID, B.AssetID, 
     Levelcode + 1 as LevelCode, 
     A.assetID + '\' + cast(A.Asset_id as varchar(20)) as Asset_Hierarchy 
FROM Asset AS a 
      INNER JOIN dbo.Batch AS Hierarchy 
      --use to get children, since the parentId of the child will be set the value 
      --of the current row 
      on a.assetId= b.assetID 
--use to get parents, since the parent of the Asset_Hierarchy row will be the asset, 
      --not the parent. 
      on Asset.AssetId= Asset_Hierarchy.parentID 


SELECT a.Assetid,a.name, 
     Asset_Hierarchy.LevelCode, Asset_Hierarchy.hierarchy 
FROM  Asset AS a 
     INNER JOIN Asset_Hierarchy 
       ON A.AssetID= Asset_Hierarchy.AssetID 
ORDER BY Hierarchy ; 
--return results from the CTE, joining to the Asset data to get the asset name 
---that is the structure you will want. I would need a little more clarification of your table structure 
+0

wow. znakomity. Dziękuję Ci! – haggercody

0

Pomoże to poznać strukturę podstawowych tabel. Istnieją dwa podejścia, które powinny działać w zależności od środowiska: SQL rozumie XML, więc możesz mieć swój SQL jako strukturę xml lub po prostu mieć pojedynczą tabelę z każdym elementem wiersza posiadającym unikalny identyfikator klucza podstawowego i parentid. id to fk dla rodzica. Dane dla węzła to tylko standardowe kolumny. Możesz użyć cte lub funkcji zasilającej kolumnę obliczeniową, aby określić stopień zagnieżdżenia dla każdego węzła. Ograniczeniem jest to, że węzeł może mieć tylko jednego rodzica.

+0

Dzięki za obejrzenie tego.
Zaktualizowałem OP za pomocą funkcji wyboru, która lepiej pokazuje strukturę tabeli. Problem jest naprawdę wokół tabeli AssetReference. Pozwól mi wyjaśnić konkretną sytuację: Rozważmy wybiorę aktywa o partii kapelusz: otrzymuję [Buntownika] - potem zaznaczyć całą gałąź drzewa dla [Buntownika] (fine tej pory) Ale potem wystąpić w AssetReference drzewo Teraz muszę wybrać oryginalny zasób z referencją A potem potrzebuję całego drzewa dla tego zasobu – haggercody

+0

Czy mówisz, że masz zestaw gałęzi dla węzła, a na jednej z tych gałęzi może być węzeł, który jest włączony zupełnie inne drzewo? –

+0

Tak, węzeł w gałęzi może "odwoływać się" do innego węzła, który może znajdować się w innej gałęzi drzewa. To "odniesienie" pochodzi z innej tabeli (AssetReference). Czy złamałem tu przykazanie o projekcie? – haggercody