2017-11-17 4 views
1

Hallo, ich versuche, eine Ansicht zu machen, die die (Mafia) Hierarchie (bis zu 5 Ebenen) aus einer einzigen Tabelle für jeden einzelnen Benutzer mit einer zusätzlichen Tabelle enthält füllen Sie den Namen jedes Benutzers aus, das ist die tatsächliche Struktur, ich benutze Fenster azurblau.T-SQL-Hierarchie von zwei Tabellen zu einer einzigen Ansicht

   [ 1 ] 
      /| \ 
     [ 2 ] [3] [4] 
     /| \  | \ 
    [5] [11] [12] [10] [9] 
     /\     \ 
    [6] [8]    [15] 
/ /\    /\ 
[7] [13] [14]   [17] [16] 
     | | 
    [19] [18] 
    /\ 
    [20] [21] 

Dies sind die Tabelle, dass ich aus den Daten erhalten:

dbo.userFamily     dbo.user 
id | user_id | parent_id  id_user | name 
------------------------  ------------------ 
1 | 2  | 1    1   | Victor 
2 | 3  | 1    2   | Lucifer 
3 | 4  | 1    3   | Hellboy 
4 | 11  | 2    4   | Spiderman 
5 | 12  | 2    5   | Martin 
6 | 10  | 4    6   | Superwoman 
7 | 9  | 4    7   | Lex Luther 
8 | 15  | 9    8   | GodMaster 
9 | 17  | 15    9   | Demon 
10 | 16  | 15    10  | balou 
11 | 6  | 5    11  | Superman 
12 | 8  | 5    12  | xman 
13 | 7  | 6    13  | hulk 
14 | 13  | 8    14  | ironman 
15 | 14  | 8    15  | aquaman 
16 | 19  | 13    16  | wonderwoman 
17 | 20  | 19    17  | batman 
18 | 21  | 19    18  | robin 
19 | 18  | 14    19  | tiger 
20 | 1  | NULL    20  | oscar 

Und das ist der Teil, ich habe bisher:

SELECT dbo.UserFamily.id, dbo.UserFamily.user_id, dbo.[User].name AS capo, 
dbo.UserFamily.parent_id AS Capo_id, NULL AS underboss, 
NULL AS underboss_id, NULL AS consigliere, NULL AS consigliere_id, 
NULL AS godfather, NULL AS godfather_id 
FROM dbo.UserFamily 
INNER JOIN dbo.[User] ON dbo.UserFamily.parent_id = dbo.[User].id_user 
ORDER BY dbo.UserFamily.user_id 

dies ist die Ansicht ich versuche zu machen (irgendwie fehlt user_id 1, was ich denke, hat damit zu tun, dass es keine parent_id gibt):

id | user_id | capo  | capo_id | underboss | underboss_id | etc.  | etc. 
----------------------------------------------------------------------------- 
1 | 2  | Victor  | 1  | NULL  | NULL   | NULL 
2 | 3  | Victor  | 1  | NULL  | NULL   | NULL 
3 | 4  | Victor  | 1  | NULL  | NULL   | NULL 
11 | 6  | Martin  | 5  | Lucifer | 2   | Ewald 
13 | 7  | Superwoman | 6  | Martin | 5   | Lucifer | 2 
12 | 8  | Martin  | 5  | Martin | 5   | Lucifer | 2 

Aber wie bekomme ich die anderen? Wie der Unterboss usw. bis hin zum Paten? Dies ist die Familienstruktur:

soldier (the user) 
capo 
underboss 
consigliere 
godfather 

Antwort

2

Rekursive CTEs sind der Trick, um solche Hierarchien zu erstellen. Hier ist ein cooler Trick für die Darstellung der Hierarchie als String:

;WITH cteBosses AS 
(
    SELECT id_user, CAST(null AS VARCHAR(100)) AS parent_name, uf.parent_id, 
     name, 1 AS level, cast(name AS VARCHAR(MAX)) AS hierarchy 
    FROM [dbo.user] u 
    INNER JOIN dbo.userFamily uf ON u.id_user = uf.user_id 
    WHERE uf.parent_id IS NULL 
    UNION ALL 
    SELECT u.id_user, CAST(c.name AS VARCHAR(100)) AS parent_name, uf.parent_id, 
     u.name, c.level + 1 AS level, cast(c.hierarchy + ' -> ' + u.name AS VARCHAR(MAX)) AS hierarchy 
    FROM [dbo.user] u 
    INNER JOIN dbo.userFamily uf ON u.id_user = uf.user_id 
    INNER JOIN cteBosses c ON uf.parent_id = c.id_user 
) 
SELECT * 
FROM cteBosses 
ORDER BY hierarchy 

Diese Ausgänge:

id_user parent  name   parent_id level hierarchy 
1  NULL  Victor  1   1  Victor 
3  Victor  Hellboy  1   2  Victor -> Hellboy 
2  Victor  Lucifer  2   2  Victor -> Lucifer 
5  Lucifer  Martin  5   3  Victor -> Lucifer -> Martin 
8  Martin  GodMaster 8   4  Victor -> Lucifer -> Martin -> GodMaster 
13  GodMaster hulk   13   5  Victor -> Lucifer -> Martin -> GodMaster -> hulk 
19  hulk  tiger  19   6  Victor -> Lucifer -> Martin -> GodMaster -> hulk -> tiger 
etc. 

lesen Steve Stedman "Common Table Expressions Joes 2 Pro" für viele große CTE Beispiele wie diese. http://stevestedman.com/2013/06/cte-scope/

die Ausgabe, die Sie generieren möchten, können Sie den CTE zu sich selbst so oft teilnehmen können, wie Sie benötigen, wie folgt aus:

;WITH cteBosses AS 
(
    SELECT id_user, CAST(null AS VARCHAR(100)) AS parent_name, uf.parent_id, 
     name, 1 AS level, cast(name AS VARCHAR(MAX)) AS hierarchy 
    FROM [dbo.user] u 
    INNER JOIN dbo.userFamily uf ON u.id_user = uf.user_id 
    WHERE uf.parent_id IS NULL 
    UNION ALL 
    SELECT u.id_user, CAST(c.name as VARCHAR(100)) AS parent_name, uf.parent_id, 
     u.name, c.level + 1 AS level, cast(c.hierarchy + ' -> ' + u.name AS VARCHAR(MAX)) AS hierarchy 
    FROM [dbo.user] u 
    INNER JOIN dbo.userFamily uf ON u.id_user = uf.user_id 
    INNER JOIN cteBosses c ON uf.parent_id = c.id_user 
) 
SELECT c1.id_user, c1.name, c2.id_user as capo_id, c2.name as capo, 
    c3.id_user as underboss_id, c3.name as underboss, 
    c4.id_user as next_boss_id, c4.name as next_boss 
FROM cteBosses c1 
LEFT JOIN cteBosses c2 ON c1.parent_id = c2.id_user 
LEFT JOIN cteBosses c3 ON c2.parent_id = c3.id_user 
LEFT JOIN cteBosses c4 ON c3.parent_id = c4.id_user 

id_user name  capo_id capo  underboss_id underboss next_boss_id next_boss 
1  Victor  NULL NULL  NULL   NULL  NULL   NULL 
2  Lucifer  1  Victor  NULL   NULL  NULL   NULL 
3  Hellboy  1  Victor  NULL   NULL  NULL   NULL 
4  Spiderman 1  Victor  NULL   NULL  NULL   NULL 
9  Demon  4  Spiderman 1    Victor  NULL   NULL 
10  balou  4  Spiderman 1    Victor  NULL   NULL 
15  aquaman  9  Demon  4    Spiderman 1    Victor 
16  wonderwoman 15  aquaman  9    Demon  4    Spiderman 
17  batman  15  aquaman  9    Demon  4    Spiderman 

ihre Empfehlung zu beantworten Frage über die Current Spalte hinzufügen, Sie kann Nest andere CTE und es für Ihre Verwendung EXISTS-Klausel:

;WITH cteBosses AS 
(
    SELECT id_user, CAST(null AS VARCHAR(100)) AS parent_name, uf.parent_id, 
     name, 1 AS level, cast(name AS VARCHAR(MAX)) AS hierarchy, id_user as top_parent, name as top_parent_name 
    FROM [dbo.user] u 
    INNER JOIN dbo.userFamily uf ON u.id_user = uf.user_id 
    WHERE uf.parent_id IS NULL 
    UNION ALL 
    SELECT u.id_user, CAST(c.name as VARCHAR(100)) AS parent_name, uf.parent_id, 
     u.name, c.level + 1 AS level, cast(c.hierarchy + ' -> ' + u.name AS VARCHAR(MAX)) AS hierarchy, 
     c.top_parent, c.top_parent_name 
    FROM [dbo.user] u 
    INNER JOIN dbo.userFamily uf ON u.id_user = uf.user_id 
    INNER JOIN cteBosses c ON uf.parent_id = c.id_user 
) 
, cteHierarchy AS 
( 
    SELECT c1.id_user, c1.name, c2.id_user as capo_id, c2.name as capo, 
     c3.id_user as underboss_id, c3.name as underboss, 
     c4.id_user as next_boss_id, c4.name as next_boss  
    FROM cteBosses c1 
    LEFT JOIN cteBosses c2 ON c1.parent_id = c2.id_user 
    LEFT JOIN cteBosses c3 ON c2.parent_id = c3.id_user 
    LEFT JOIN cteBosses c4 ON c3.parent_id = c4.id_user 
) 
select id_user, name, --*, 
case 
    when exists (select * from cteHierarchy cInner where next_boss = cOuter.name) then 'next_boss' 
    when exists (select * from cteHierarchy cInner where underboss = cOuter.name) then 'underboss' 
    when exists (select * from cteHierarchy cInner where capo = cOuter.name) then 'capo' 
    else 'regular_dude' 
end as CurrentPosition 
from cteHierarchy cOuter 

id_user name  CurrentPosition 
1   Victor next_boss 
4   Spiderman next_boss 
9   Demon  underboss 

Sie halten Ebenen der CTE wie diese Zugabe - ein CTE der CTEs verweisen kann, die vor ihm kommen (aber nicht nach). Sie geben also im Grunde einen Tabellenalias für die Abfrageausgabe und verwenden dann EXISTS, um den CurrentPosition-Wert zu berechnen.

+0

danke das ist extrem cool, aber wenn ich versuche, mit Ihnen zu arbeiten Beispiel, die untere, ist der folgende Fehler: Msg 240, Ebene 16, Zustand 1, Linie 1 Typen nicht zwischen den Anker übereinstimmen und der rekursive Teil in der Spalte "parent_name" der rekursiven Abfrage "cteBosses". –

+0

@EwaldBos Probieren Sie es jetzt aus - für eine rekursive Abfrage müssen die anchor (top) und rekursive (bottom) Queries identische Datentypen in jeder Spalte haben. Ich habe die Abfrage geändert, um die parent_name Spalte in jede Abfrage in ein VARCHAR (100) umzuwandeln , jetzt stimmen die Typen überein. (Ich hatte ein VARCHAR (25) angenommen, aber das stimmt nicht mit der Definition für die Namensspalte in Ihrer Tabelle überein.) –

+0

ja es funktioniert perfekt, vielen Dank für die Erklärung und die Zeit, die Sie eingingen. –

Verwandte Themen