加入动态数据透视(版本2)

时间:2009-05-24 17:08:41

标签: sql sql-server sql-server-2008 join

我有一些数据表:

类别

CategoryID     CategoryName

1              Home
2              Contact
3              About

位置

PositionID     PositionName

1              Main menu
2              Left menu
3              Right menu

...(稍后可以添加新行)

CategoryPosition

CPID   CID    PID    COrder

1      1      1      1 
2      1      2      2
3      1      3      3
4      2      1      4
5      2      3      5

如何制作这样的表:

CID    CName     MainMenu   LeftMenu   RightMenu

1      Home      1          2          3
2      Contact   4          0          5
3      About     0          0          0

如果稍后添加新的类别或位置行,则查询应自动反映更改,例如:

CID    CName     MainMenu   LeftMenu   RightMenu   BottomMenu

1      Home      1          2          3           0
2      Contact   4          0          5           0
3      About     0          0          0           0
4      News      0          0          0           0

5 个答案:

答案 0 :(得分:2)

以下动态查询似乎有效:

declare @columnlist nvarchar(4000)
select @columnlist = IsNull(@columnlist + ', ', '') + '[' + PositionName + ']'
from #Position

declare @query nvarchar(4000)
select @query = '
    select *
    from (
        select CategoryId, CategoryName, PositionName, 
                IsNull(COrder,0) as COrder
        from #Position p
        cross join #Category c
        left join #CategoryPosition cp 
                on cp.pid = p.PositionId 
                and cp.cid = c.CategoryId
    ) pv
    PIVOT (max(COrder) FOR PositionName in (' + @columnlist + ')) as Y
    ORDER BY CategoryId, CategoryName
'

exec sp_executesql @query

一些澄清:

  • @columnlist包含根据位置表
  • 构建的动态字段列表
  • 交叉联接会创建所有类别和所有职位的列表
  • 左连接寻找相应的COrder
  • max()选择每个类别+位置的最高COrder(如果有多个
  • PIVOT()将各种PositionNames转换为单独的列

P.S。我的表名以#开头,因为我将它们创建为临时表。删除#以引用永久表。

P.S.2。如果有人想尝试这个,这里有一个脚本来创建这个问题的表:

set nocount on 

if object_id('tempdb..#Category') is not null drop table #Category
create table #Category (
    CategoryId int identity,
    CategoryName varchar(50)
)

insert into #Category (CategoryName) values ('Home')
insert into #Category (CategoryName) values ('Contact')
insert into #Category (CategoryName) values ('About')
--insert into #Category (CategoryName) values ('News')

if object_id('tempdb..#Position') is not null drop table #Position
create table #Position (
    PositionID int identity,
    PositionName varchar(50)
)

insert into #Position (PositionName) values ('Main menu')
insert into #Position (PositionName) values ('Left menu')
insert into #Position (PositionName) values ('Right menu')
--insert into #Position (PositionName) values ('Bottom menu')

if object_id('tempdb..#CategoryPosition') is not null 
    drop table #CategoryPosition
create table #CategoryPosition (
    CPID int identity,
    CID int,
    PID int,
    COrder int
)

insert into #CategoryPosition (CID, PID, COrder) values (1,1,1)
insert into #CategoryPosition (CID, PID, COrder) values (1,2,2)
insert into #CategoryPosition (CID, PID, COrder) values (1,3,3)
insert into #CategoryPosition (CID, PID, COrder) values (2,1,4)
insert into #CategoryPosition (CID, PID, COrder) values (2,3,5)

答案 1 :(得分:1)

由于PIVOT需要一个静态列列表,我认为基于动态sql的方法实际上就是你可以做的所有事情:http://www.simple-talk.com/community/blogs/andras/archive/2007/09/14/37265.aspx

答案 2 :(得分:1)

正如几张海报所提到的,使用PIVOT命令的动态SQL是可行的方法。我写了一个名为pivot_query.sql的存储过程,这对于此目的非常方便。它的工作原理如下:

-- Define a query of the raw data and put it in a variable (no pre-grouping required)
declare @myQuery varchar(MAX);

set @myQuery = '
select
   cp.cid,
   c.CategoryName,
   p.PositionName,
   cp.COrder
from
   CategoryPosition cp

   JOIN Category c
      on (c.CategoryId = cp.cid)

   JOIN Position p
      on (p.PositionId = cp.pid)';

-- Call the proc, passing the query, row fields, pivot column and summary function
exec dbo.pivot_query @myQuery, 'CategoryName', 'PositionName', 'max(COrder) COrder'

pivot_query调用的完整语法是:

pivot_query '<query>', '<field list for each row>', '<pivot column>', '<aggregate expression list>', '[<results table>]', '[<show query>]'

source code顶部的评论中对此进行了解释。

此过程的一些优点是您可以指定多个汇总函数,如max(COrder),min(COrder)等。它可以选择将输出存储在表中,以防您想要加入汇总数据与其他信息。

答案 3 :(得分:0)

我猜你需要选择使用PIVOT。默认情况下,pivotots仅选择静态列列表。网上有一些处理动态列枢轴的解决方案,例如herehere

答案 4 :(得分:0)

我的建议是将您的数据作为简单连接返回,并让前端对其进行排序。 SQL有一些优秀的东西,但这个特殊问题似乎是前端应该做的事情。当然,我不知道如果不知道你的情况,那就是我的预感。