2009-12-24 5 views
2

Asume de Let j'ai une configuration de structure parent-enfant dans SQL (serveur 2005):Obtenez un parent seul avec tous les enfants en une seule rangée

CREATE TABLE parent (Id INT IDENTITY PRIMARY KEY, Name VARCHAR(255)) 
CREATE TABLE child (Id INT IDENTITY PRIMARY KEY, parentId INT, Name VARCHAR(255)) 

insert into parent select 'parent with 1 child' 
insert into parent select 'parent with 2 children' 

insert into child(name, parentid) select 'single child of parent 1', 1 
insert into child(name, parentid) select 'child 1 of 2 of parent 2', 2 
insert into child(name, parentid) select 'child 2 of 2 of parent 2', 2 

est-il un moyen de retourner une ligne par parent avec ses les enfants comme des colonnes? Comme:

parent.Id, parent.Name, enfant (1) .id, enfant (1) .Name, enfant (2) .id, enfant (2) .Nom

Commencé avec:

select * from parent p 
    left outer join child c1 on c1.parentid = p.id 
+0

pourrait marquer une réponse sur ce point pour les futurs visiteurs s'il vous plaît? – Tanner

Répondre

4

Votre exemple est proche de pivoter, mais je ne pense pas que la fonctionnalité de pivot est utilisable sur celui-ci.

J'ai renommé votre exemple pour utiliser "department-person", au lieu de "child-parent", juste pour garder ma santé mentale.

Ainsi, les premières tables et des données

DECLARE @Department TABLE 
    ( 
    DepartmentID int 
    ,DepartmentName varchar(50) 
) 
DECLARE @Person TABLE 
    ( 
    PersonID int 
    ,PersonName varchar(50) 
    ,DepartmentID int 
) 

INSERT INTO @Department 
    (DepartmentID, DepartmentName) 
SELECT 1, 'Accounting' UNION 
SELECT 2, 'Engineering' UNION 
SELECT 3, 'Sales' UNION 
SELECT 4, 'Marketing' ; 

INSERT INTO @Person 
    (PersonID, PersonName, DepartmentID) 
SELECT 1, 'Lyne', 1 UNION 
SELECT 2, 'Damir', 2 UNION 
SELECT 3, 'Sandy', 2 UNION 
SELECT 4, 'Steve', 3 UNION 
SELECT 5, 'Brian', 3 UNION 
SELECT 6, 'Susan', 3 UNION 
SELECT 7, 'Joe', 4 ; 

Maintenant, je veux aplatir le modèle, je vais utiliser la table temporaire parce que j'ai les variables de table - mais une vue sur les « tables réelles » serait bon aussi.

/* Create a table with: 
    DepartmentID, DepartmentName, PersonID, PersonName, PersonListIndex 

This could be a view instead of temp table. 
*/ 
IF object_id('tempdb.dbo.#tmpTbl','U') IS NOT NULL 
DROP TABLE #tmpTbl 

; 
WITH prs 
     AS (SELECT PersonID 
        ,PersonName 
        ,DepartmentID 
        ,row_number() OVER (PARTITION BY DepartmentID ORDER BY PersonID) AS [PersonListIndex] 
      FROM @Person 
      ), 
     dptprs 
     AS (SELECT d.DepartmentID 
        ,d.DepartmentName 
        ,p.PersonID 
        ,p.PersonName 
        ,p.PersonListIndex 
      FROM @Department AS d 
        JOIN prs AS p ON p.DepartmentID = d.DepartmentID 
      ) 
SELECT * INTO #tmpTbl FROM dptprs 

-- SELECT * FROM #tmpTbl 

colonnes dynamiques signifie requête dynamique, je vais composer ligne par ligne dans une table

/* Table to compose dynamic query */ 
DECLARE @qw TABLE 
    ( 
    id int IDENTITY(1, 1) 
    ,txt nvarchar(500) 
) 

/* Start composing dynamic query */ 
INSERT INTO @qw (txt) VALUES ('SELECT') 
INSERT INTO @qw (txt) VALUES ('[DepartmentID]') 
INSERT INTO @qw (txt) VALUES (',[DepartmentName]') ; 


/* fetch max number of employees in a department */ 
DECLARE @i int ,@m int 
SET @m = (SELECT max(PersonListIndex) FROM #tmpTbl) 

/* Compose dynamic query */ 
SET @i = 1 
WHILE @i <= @m 
    BEGIN 
     INSERT INTO @qw (txt) 
      SELECT ',MAX(CASE [PersonListIndex] WHEN ' 
        + cast(@i AS varchar(10)) + ' THEN [PersonID] ELSE NULL END) AS [Person_' 
        + cast(@i AS varchar(10)) + '_ID]' 

     INSERT INTO @qw (txt) 
      SELECT ',MAX(CASE [PersonListIndex] WHEN ' 
        + cast(@i AS varchar(10)) + ' THEN [PersonName] ELSE NULL END) AS [Person_' 
        + cast(@i AS varchar(10)) + '_Name]' 

    SET @i = @i + 1 
    END 

/* Finish the dynamic query */ 
INSERT INTO @qw (txt) VALUES ('FROM #tmpTbl') 
INSERT INTO @qw (txt) VALUES ('GROUP BY [DepartmentID], [DepartmentName]') 
INSERT INTO @qw (txt) VALUES ('ORDER BY [DepartmentID]') 

-- SELECT * FROM @qw 

Et maintenant, concaténer toutes les lignes de requête dans une variable et exécuter

/* Create a variable with dynamic sql*/ 
DECLARE @exe nvarchar(4000) 
SET @exe='' 
SELECT @exe = @exe + txt + ' ' FROM @qw ORDER BY id 

/* execute dynamic sql */ 
EXEC master..sp_executesql @exe 

Et voici le résultat:

alt text http://www.damirsystems.com/dp_images/morepivot_result_01.png

+0

Bel exemple! Je verrai si je peux l'utiliser sur mes données réelles. Merci! – edosoft

+0

J'avais besoin de faire quelque chose de similaire. On m'a donné une feuille de calcul Excel avec les données de deux tables aplaties dans une seule table. J'ai utilisé ce code pour faire pivoter les lignes enfant vers les colonnes. Tu m'as sauvé une tonne de temps. Merci!!! –

1

essai avec pivotement dynamique

données de test

declare @parent table (Id INT IDENTITY PRIMARY KEY, Name VARCHAR(255)) 
declare @child table (Id INT IDENTITY PRIMARY KEY, parentId INT, Name VARCHAR(255)) 

insert into @parent select 'parent with 1 child' 
insert into @parent select 'parent with 2 children' 
insert into @child(name, parentid) select 'single child of parent 1', 1 
insert into @child(name, parentid) select 'child 1 of 2 of parent 2', 2 
insert into @child(name, parentid) select 'child 2 of 2 of parent 2', 2 

de requête

declare @col_list varchar(max) 
declare @dynquery nvarchar(max) 
select 
c.Id as ChildId 
, p.Id as ParentId 
,p.Name as ParentName 
,c.Name as ChildName 
into #t from @parent p join @child c on p.Id = c.parentId 
select @col_list = stuff(cols,1,1,'') from 
(select distinct ',[' + cast(ChildName as varchar(50)) + ']' 
from #t for xml path(''))X(cols) 
set @dynquery = 'select * from #t pivot (max(ChildId) for ChildName in (' + @col_list + ') ' + ') as pvt' 
EXEC master..sp_executesql @dynquery 
drop table #t 

sortie:

ParentId ParentName child 1 of 2 of parent 2 child 2 of 2 of parent 2 single child of parent 1 
1 parent with 1 child NULL NULL 1 
2 parent with 2 children 2 3 NULL 
3

Si vous voulez un look différent essayez cette

Données d'échantillons

declare @parent table (Id INT IDENTITY PRIMARY KEY, Name VARCHAR(255)) 
declare @child table (Id INT IDENTITY PRIMARY KEY, parentId INT, Name VARCHAR(255)) 

insert into @parent select 'parent with 1 child' 
insert into @parent select 'parent with 2 children' 
insert into @child(name, parentid) select 'single child of parent 1', 1 
insert into @child(name, parentid) select 'child 1 of 2 of parent 2', 2 
insert into @child(name, parentid) select 'child 2 of 2 of parent 2', 2 

Recherche

select p.Id as ParentId,p.Name as ParentName 
    ,stuff((select ', ' + 'child(' + cast(c.Id as varchar(10)) + ') : ' + c.Name 
    from @child c where c.parentId = p.id for xml path('')),1,1,'') 'Child(id):Child Names' 
    from @parent p 
group by 
p.Id,p.Name 

Sortie

ParentId ParentName Child(id):Child Names 
1 parent with 1 child child(1) : single child of parent 1 
2 parent with 2 children child(2) : child 1 of 2 of parent 2, child(3) : child 2 of 2 of parent 2 
1

Après avoir examiné la réponse de Damir Sudarevic je suis venu avec ceci: (voir ma question pour la structure de table)

declare @family table (parentid int , parentname varchar(255), child1id int, child1name varchar(255), child2id int, child2name varchar(255)) 

insert into @family(parentid, parentname) 
select id, name from parent 

update @family set child1id = c.id, child1name = c.name 
from @family f, child c 
where c.parentid = f.parentid 

update @family set child2id = c.id, child2name = c.name 
from @family f, child c 
where c.parentid = f.parentid 
and not exists 
    (select child1id from @family where child1id = c.id) 

select * from @family 
Questions connexes