2016-11-15 4 views
5

У меня есть таблица выглядит следующим образом:Сформировать список номер

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

Я хочу, чтобы преобразовать таблицу в список, как это:

+-------+--------+ 
| 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 | 
+-------+--------+ 

Я попытался rollup с row_number, но результат все равно далеко, что я хочу

;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 

Заранее благодарен.

EDIT
Вот SQL для создания таблицы, надеюсь, что это может помочь

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 должна быть Анна? – Eric

+0

Да, извините за опечатку, спасибо – Prisoner

+0

FWIW упорядочение в таблице результатов не совсем корректно. Например, не должно быть 1.1.1 Алиса и 1.1.2 быть Том? –

ответ

2

DENSE_RANK Это использует, чтобы получить правильный нумерации для ID. Затем введите CROSS APPLY to unpivot данные и отметьте, какая строка для Grp, Party, или Member. Наконец использовать WHERE фильтровать только те строки, которые нужно:

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

Если заказ не имеет значения для Member заменить rnMember с:

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

ONLINE DEMO

+0

Спасибо, могу я знать, будет ли 'cross apply' дороже, чем' union all'? Из статистики _client_ кажется, что 'union all' немного лучше – Prisoner

+0

В статье, на которой указано, показывает, что' CROSS APPLY' является лучшим методом. Однако мой ответ полностью отличается от других, поскольку мне не нужно использовать 'DISTINCT' 3 раза. Попробуйте выполнить тестирование для большего количества строк. –

2

Вот один из способов

;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] 

Вы должны использовать DENSE_RANK для родителей, чтобы сформировать иерархию правильно. Если у вас есть дубликаты в Member, а затем изменить ROW_NUMBER к DENSE_RANK внутри CTE и добавить отличие от окончательного select запроса

Примечание: Если вы используете что-то меньшее, чем SQL SERVER 2012 затем использовать + оператор для конкатенации вместо CONCAT

+0

Спасибо @Prdp, вы отвечаете, но ответ Felix Pamittan сортирует его более правильно – Prisoner

1

Честно говоря, я бы не сделал этого на уровне базы данных. Вместо этого я гарантирую, что результат сортируется по {Grp, Party, Member}, а затем присваивает значения «Id» за один проход при отображении данных.

Однако, если вы решили сделать это на сервере базы данных по какой-либо причине, вы можете использовать функцию dense_rank(), чтобы получить каждый индивидуальный идентификатор:

;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

будет 'dense_rank()' дорогим, чем цикл всех записей, чтобы назначить 'id' в программировании? – Prisoner

1

Этот параметр не используется DENSE_RANK() но ROW_NUMBER() но по существу аналогичен другим опубликованным ответам.

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

Не предлагайте псевдонимы, подобные этому –

+0

@Prdp, как вы это понимаете? – mendosi

+1

Вместо этого 'MyTable.Member ', добавьте псевдоним в свою таблицу' MyTable MT' и используйте его для ссылки 'MT.Member'. Это более читаемо –

Смежные вопросы