2016-11-15 11 views
5

Mam tabeli wyglądać następująco:Generowanie lista numer

+-------+--------+--------+ 
| Grp | Party | Member | 
+-------+--------+--------+ 
| FC | Party1 | Tom | 
| FC | Party1 | Alice | 
| FC | Party2 | John | 
| FC | Party3 | Mary | 
| GC | Party2 | Anna | 
| GC | Party4 | Alex | 
| GC | Party5 | Diana | 
+-------+--------+--------+ 

Chcę przekształcić tablicę do listy tak:

+-------+--------+ 
| ID | Text | 
+-------+--------+ 
| 1  | FC  | 
| 1.1 | Party1 | 
| 1.1.1 | Tom | 
| 1.1.2 | Alice | 
| 1.2 | Party2 | 
| 1.2.1 | John | 
| 1.3 | Party3 | 
| 1.3.1 | Mary | 
| 2  | GC  | 
| 2.1 | Party2 | 
| 2.1.1 | Anna | 
| 2.2 | Party4 | 
| 2.2.1 | Alex | 
| 2.3 | Party5 | 
| 2.3.1 | Diana | 
+-------+--------+ 

Próbowałem rollup z row_number, ale wynik nadal daleko, co chcę

;with ctx as (
    select * from @test 
    group by rollup(Grp, Party, Member) 
) 
select row_number() over (partition by grp order by grp, party, member) as g, 
     row_number() over (partition by grp, party order by grp, party, member) as p, 
     row_number() over (partition by grp, party, member order by grp, party, member) as m, 
     grp, party, member 
from ctx 
where grp is not null 
order by grp, party, member 

Z góry dziękuję.

EDIT
Oto SQL do generowania tabeli, nadzieję, że może to pomóc

declare @test table (Grp varchar(10), Party varchar(10), Member varchar(20)) 

insert into @test values ('FC', 'Party1', 'Tom') 
insert into @test values ('FC', 'Party1', 'Alice') 
insert into @test values ('FC', 'Party2', 'John') 
insert into @test values ('FC', 'Party3', 'Mary') 
insert into @test values ('GC', 'Party2', 'Anna') 
insert into @test values ('GC', 'Party4', 'Alex') 
insert into @test values ('GC', 'Party5', 'Diana') 
+0

2.1.1 powinna być Anna, prawda? – Eric

+0

Tak, przepraszam za literówkę, dzięki – Prisoner

+0

FWIW zamawianie w tabeli wyników nie wydaje się właściwe. Na przykład, czy 1.1.1 nie powinien być Alice, a 1.1.2 być Tomem? –

Odpowiedz

2

używa DENSE_RANK aby uzyskać prawidłowy numeracji dla ID. Następnie dane i zaznacz, który wiersz jest dla Grp, Party lub Member. Wreszcie używać WHERE filtrować tylko te wiersze, których potrzebujesz:

WITH CteUnpivot AS(
    SELECT * 
    FROM (
     SELECT *, 
      rnGrp  = DENSE_RANK() OVER(ORDER BY Grp), 
      rnParty  = DENSE_RANK() OVER(PARTITION BY Grp ORDER BY Party), 
      rnMember = ROW_NUMBER() OVER(PARTITION BY Grp, Party ORDER BY Member) 
     FROM test 
    ) t 
    CROSS APPLY(VALUES 
     ('Grp', Grp), 
     ('Party', Party), 
     ('Member', Member) 
    ) x (col, [Text]) 
) 
SELECT 
    ID = CASE 
      WHEN col = 'Grp' THEN CAST(rnGrp AS VARCHAR(3)) 
      WHEN col = 'Party' THEN CAST(rnGrp AS VARCHAR(3)) + '.' + CAST(rnParty AS VARCHAR(3)) 
      WHEN col = 'Member' THEN CAST(rnGrp AS VARCHAR(3)) + '.' + CAST(rnParty AS VARCHAR(3)) + '.' + CAST(rnMember AS VARCHAR(3)) 
     END, 
    [Text] 
FROM CteUnpivot 
WHERE 
    (col = 'Grp' AND rnParty = 1 AND rnMember = 1) 
    OR (col = 'Party' AND rnMember = 1) 
    OR (col = 'Member') 
ORDER BY rnGrp, rnParty, rnMember; 

ONLINE DEMO

Jeżeli kolejność nie ma znaczenia dla Member wymienić rnMember z:

rnMember = ROW_NUMBER() OVER(PARTITION BY Grp, Party ORDER BY (SELECT NULL)) 

ONLINE DEMO

+0

Dzięki, czy mogę wiedzieć, że "zastosowanie krzyża" będzie droższe niż "unia wszystko"? Ze statystyk _client_, wydaje się, że 'union all' jest nieco lepszy – Prisoner

+0

Z powyższego artykułu wynika, że" CROSS APPLY "jest najlepszą metodą. Jednak moja odpowiedź jest zupełnie inna od innych, ponieważ moja nie musi używać "DISTINCT" 3 razy. Spróbuj przetestować większą liczbę wierszy. –

2

Oto jeden sposób

;WITH cte 
    AS (SELECT Dense_rank()OVER (ORDER BY grp) AS g, 
       Dense_rank()OVER (partition BY grp ORDER BY party) AS p, 
       Row_number()OVER (partition BY grp, party ORDER BY member) AS m, 
       grp, 
       party, 
       member 
     FROM @test 
     WHERE grp IS NOT NULL) 
SELECT DISTINCT grp, 
       Cast(g AS VARCHAR(10)) AS [Text] 
FROM cte 
UNION ALL 
SELECT DISTINCT party, 
       Concat(g, '.', p) 
FROM cte 
UNION ALL 
SELECT member, 
     Concat(g, '.', p, '.', m) 
FROM cte 
ORDER BY [Text] 

Trzeba użyć DENSE_RANK dla rodziców, aby generować hierarchii numery poprawnie. Jeśli masz duplikaty w Member także wtedy zmienić ROW_NUMBER do DENSE_RANK wewnątrz CTE i dodać odrębne do końcowego select zapytania

Uwaga: Jeśli używasz czegoś mniej niż SQL SERVER 2012 następnie użyć + operatorem konkatenacji zamiast CONCAT

+0

Dzięki @Prdp, odpowiedź jest świetna, ale odpowiedź Felixa Pamittana sortuj go bardziej poprawnie – Prisoner

1

Szczerze mówiąc, nie zrobiłbym tego na poziomie bazy danych. Zamiast tego zapewniam sortowanie danych wyjściowych według {Grp, Party, Członek}, a następnie przypisuję wartości "Id" w jednym przejściu podczas wyświetlania danych.

Jednakże, jeśli jesteś zdeterminowany, aby zrobić to na serwerze bazy danych, z jakiegokolwiek powodu, można użyć funkcji dense_rank() aby każdy indywidualny ID:

;with cte as (
    select dense_rank() over (order by Grp) id0, 
    dense_rank() over (partition by Grp order by Party) id1, 
    dense_rank() over (partition by Grp, Party order by Member) id2, 
    Grp, Party, Member 
    from Table1 
), grps as (select distinct id0, Grp from cte), 
parties as (select distinct id0, id1, Party from cte), 
members as (select distinct id0, id1, id2, Member from cte), 
[list] as (
    select cast(id0 as varchar(50)) as id, Grp as [Text] from grps 
    union all 
    select cast(id0 as varchar(50)) + '.' + cast(id1 as varchar(50)), Party from parties 
    union all 
    select cast(id0 as varchar(50)) + '.' + cast(id1 as varchar(50)) + '.' + cast(id2 as varchar(50)), Member from members 
) 
select id, [Text] 
from [list] 
order by id 
+0

będzie "dense_rank()' droga niż pętla wszystkie rekordy, aby przypisać "id" w programowaniu? – Prisoner

1

Ta opcja nie używa DENSE_RANK() ale ROW_NUMBER() ale jest zasadniczo podobny do innych opublikowanych odpowiedzi.

With grps As (
    Select Grp, GrpNo = Row_Number() Over (Order By Grp) 
     From (Select Distinct Grp From MyTable) As MyTable), 
parties As (
    Select MyTable.Grp, MyTable.Party, grps.GrpNo, PrtyNo = Row_Number() Over (Partition By MyTable.Grp Order By MyTable.Party) 
     From (Select Distinct Grp, Party From MyTable) As MyTable 
     Join grps On MyTable.Grp = grps.Grp), 
members As (
    Select MyTable.Grp, MyTable.Party, MyTable.Member, 
     parties.GrpNo, parties.PrtyNo, MbrNo = Row_Number() Over (Partition By MyTable.Grp, MyTable.Party Order By #groups.Member) 
     From MyTable 
     Join parties On MyTable.Grp = parties.Grp And MyTable.Party = parties.Party) 
Select ID = Convert(char(5), GrpNo), 
    [Text] = Grp 
    From grps 
Union All 
Select ID = Convert(char(1), GrpNo) + '.' + Convert(char(1), PrtyNo), 
    [Text] = Party 
    From parties 
Union All 
Select ID = Convert(char(1), GrpNo) + '.' + Convert(char(1), PrtyNo) + '.' + Convert(char(1), MbrNo), 
    [Text] = Member 
    From members; 
+0

Nie sugeruj nazw takich jak –

+0

@Prdp jak masz na myśli? – mendosi

+1

Zamiast tego 'MyTable.Member,' dodaj nazwę aliasu do swojej tabeli 'MyTable MT' i użyj jej dla odniesienia' MT.Member'. Jest bardziej czytelny –