如何从3个表创建数据透视表?

时间:2013-07-12 22:00:19

标签: sql sql-server pivot-table

我在查看地址信息方面遇到了问题。

示例

  • 我有一个人或关系超过1个地址。
  • 我将每个地址存储在地址表中,并定义它是什么类型的地址。
  • 我有3个地址类型作为示例。

我需要做的是将彼此相邻的所有地址列可视化。

如果我只有一个地址,则其他字段必须显示NULL,因为没有地址。

我在这里创建了一个小表设置。

Create table relation(
PKid int identity(1,1) primary key,
Name varchar(255)
)

--Create table Adrestype(
PKid int identity(1,1) primary key,
TypeDescription varchar(255)
)

Create table adres(
PKid int identity(1,1) primary key,
Street varchar(255),
Number varchar(255),
zipcode varchar(255),
Location varchar(255),
AdresTypeId int
)

Create table RelationXAdres(
PKid int identity(1,1) primary key,
RelationID int not null,
adresID int not null
)

Insert into Relation values('Peter');
Insert into Relation values('Nico');
Insert into Relation values('Bart');
Insert into Relation values('Werner');

Insert into Adrestype values('Work');
Insert into Adrestype values('Home');
Insert into Adrestype values('Extra');

Insert into adres values ('Streetname', '125', '5520', 'Gent', 1)
Insert into adres values ('StreetLane', '15', '5550', 'Rome', 2)
Insert into adres values ('Street', '12', '5120', 'Paris', 3)
Insert into RelationXAdres values( 1,1);
Insert into RelationXAdres values( 1,2);
Insert into RelationXAdres values( 1,3);

Insert into adres values ('againstraat', '5', '4420', 'Oslo', 1)
Insert into adres values ('some Street', '12', '2220', 'Praag', 2)
Insert into RelationXAdres values( 2,4);
Insert into RelationXAdres values( 2,5);

Insert into adres values ('SoloStreet', '5', '4420', 'Oslo', 1)
Insert into RelationXAdres values( 3,6);

Insert into adres values ('MainStreet', '25', '1120', 'Berlin', 3)
Insert into RelationXAdres values( 4,7);

-- show all tabel's data
select * from relation
Select * from adres
select * from RelationXAdres
select * from Adrestype

-- Show all data in 1 statement
select * from relation r
left join RelationXAdres ra on ra.RelationID = r.PKid
left join adres a on a.PKid = ra.adresId
left join adrestype at on at.PKid = a.AdresTypeId

结果必须如下:

enter image description here

1 个答案:

答案 0 :(得分:2)

由于您使用的是SQL Server,因此有几种方法可以将数据行转移到列中。

您可以将聚合函数与CASE表达式一起使用:

select r.pkid,
  r.name,
  max(case when at.typedescription = 'home' then a.street end) homestreet,
  max(case when at.typedescription = 'home' then a.number end) homeNumber,
  max(case when at.typedescription = 'home' then a.zipcode end) homezipcode,
  max(case when at.typedescription = 'home' then a.location end) homelocation,
  max(case when at.typedescription = 'work' then a.street end) workstreet,
  max(case when at.typedescription = 'work' then a.number end) workNumber,
  max(case when at.typedescription = 'work' then a.zipcode end) workzipcode,
  max(case when at.typedescription = 'work' then a.location end) worklocation,
  max(case when at.typedescription = 'extra' then a.street end) extrastreet,
  max(case when at.typedescription = 'extra' then a.number end) extraNumber,
  max(case when at.typedescription = 'extra' then a.zipcode end) extrazipcode,
  max(case when at.typedescription = 'extra' then a.location end) extralocation
from relation r
left join RelationXAdres ra
  on r.pkid = ra.RelationID
left join adres a
  on ra.adresid = a.pkid
left join adrestype at
  on a.AdresTypeId = at.PKid
group by r.pkid, r.name;

请参阅SQL Fiddle with Demo

您可以同时应用UNPIVOTPIVOT功能。 UNPIVOT函数将使用streetnumberzipcodelocation的多列,并将它们转换为多行。

select pkid, name,
  col = typeDescription+col,
  value
from
(
  select r.pkid,
    r.name,
    at.typedescription,
    a.street,
    a.number,
    a.zipcode,
    a.location
  from relation r
  left join RelationXAdres ra
    on r.pkid = ra.RelationID
  left join adres a
    on ra.adresid = a.pkid
  left join adrestype at
    on a.AdresTypeId = at.PKid
) d
unpivot
(
  value
  for col in (street, number, zipcode, location)
) unpiv;

SQL Fiddle with Demo。这给出了多行的结果:

| PKID |   NAME |           COL |       VALUE |
-----------------------------------------------
|    1 |  Peter |    Workstreet |  Streetname |
|    1 |  Peter |    Worknumber |         125 |
|    1 |  Peter |   Workzipcode |        5520 |
|    1 |  Peter |  Worklocation |        Gent |
|    1 |  Peter |    Homestreet |  StreetLane |
|    1 |  Peter |    Homenumber |          15 |

一旦数据在多行中,您就可以应用PIVOT函数:

;with cte as
(
  select pkid, name,
    col = typeDescription+col,
    value
  from
  (
    select r.pkid,
      r.name,
      at.typedescription,
      a.street,
      a.number,
      a.zipcode,
      a.location
    from relation r
    left join RelationXAdres ra
      on r.pkid = ra.RelationID
    left join adres a
      on ra.adresid = a.pkid
    left join adrestype at
      on a.AdresTypeId = at.PKid
  ) d
  unpivot
  (
    value
    for col in (street, number, zipcode, location)
  ) unpiv
) 
select pkid, name,
  homestreet, homenumber, homezipcode, homelocation,
  workstreet, worknumber, workzipcode, worklocation,
  extrastreet, extranumber, extrazipcode, extralocation
from cte
pivot
(
  max(value)
  for col in (homestreet, homenumber, homezipcode, homelocation,
              workstreet, worknumber, workzipcode, worklocation,
              extrastreet, extranumber, extrazipcode, extralocation)
) p;

请参阅SQL Fiddle with Demo

如果你有一个已知的列数,上面的版本将会很好用,但是如果你有一个未知数量的值(地址类型),那么你将需要使用动态SQL:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME(a.TypeDescription+c.col) 
                    from Adrestype a
                    cross apply
                    (
                      select 'street', 1 union all
                      select 'number', 2 union all
                      select 'zipcode', 3 union all
                      select 'location', 4 
                    ) c (col, so)
                    group by a.TypeDescription, c.col, c.so
                    order by case a.TypeDescription
                              when 'home' then 1
                              when 'work' then 2
                              when 'extra' then 3 end, c.so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT pkid, name, ' + @cols + ' 
            from 
            (
                select pkid, name,
                  col = typeDescription+col,
                  value
                from
                (
                  select r.pkid,
                    r.name,
                    at.typedescription,
                    a.street,
                    a.number,
                    a.zipcode,
                    a.location
                  from relation r
                  left join RelationXAdres ra
                    on r.pkid = ra.RelationID
                  left join adres a
                    on ra.adresid = a.pkid
                  left join adrestype at
                    on a.AdresTypeId = at.PKid
                ) d
                unpivot
                (
                  value
                  for col in (street, number, zipcode, location)
                ) unpiv
            ) x
            pivot 
            (
                max(value)
                for col in (' + @cols + ')
            ) p '

execute sp_executesql @query;

SQL Fiddle with Demo。所有这些查询都会产生结果:

| PKID |   NAME |  HOMESTREET | HOMENUMBER | HOMEZIPCODE | HOMELOCATION |  WORKSTREET | WORKNUMBER | WORKZIPCODE | WORKLOCATION | EXTRASTREET | EXTRANUMBER | EXTRAZIPCODE | EXTRALOCATION |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|    1 |  Peter |  StreetLane |         15 |        5550 |         Rome |  Streetname |        125 |        5520 |         Gent |      Street |          12 |         5120 |         Paris |
|    2 |   Nico | some Street |         12 |        2220 |        Praag | againstraat |          5 |        4420 |         Oslo |      (null) |      (null) |       (null) |        (null) |
|    3 |   Bart |      (null) |     (null) |      (null) |       (null) |  SoloStreet |          5 |        4420 |         Oslo |      (null) |      (null) |       (null) |        (null) |
|    4 | Werner |      (null) |     (null) |      (null) |       (null) |      (null) |     (null) |      (null) |       (null) |  MainStreet |          25 |         1120 |        Berlin |