SQL查询 - 将表的行转换为列

时间:2009-11-18 04:06:25

标签: sql

我有一个奇怪的SQL问题。

我有2个表,比如产品(带有列pid和pname)和规则(带有列rid和rname),这两个表没有任何共同之处。 product表有100行,规则表有16行。

我需要以这样的方式编写查询:我获得的结果将pname作为第一列,其余列是表规则的16行。换句话说,我需要在结果的列中转换表规则的行。因此,在结果中,我应该能够有100行(产品表中的所有pnames)和17列(pname和规则表中的16行)。

你能帮我写一下这样的疑问吗?我尝试使用case语句将行转换为列,但是当与product表的结果结合使用时,我得到的行数是产品和规则表的笛卡尔积,我不想要。我希望行数等于product表中的行,并且列数等于规则表中的行。你能帮忙吗?

4 个答案:

答案 0 :(得分:1)

如果您使用的是SQL Server 2005+,则可以使用Pivot和Unpivot命令将行转换为列,将列转换为行。

答案 1 :(得分:1)

这称为Pivot。要拥有动态数量的列,您可能必须使用动态SQL-从另一个查询动态创建查询,并使用任何可用的eval方法。根据您使用的语言,调用代码可以轻松处理。我建议重构以另一种方式进行,或者为几个小时的学习做好准备。如果没有更多信息 - 数据库模式,以及您正在使用的sql db,我无法真正帮助您查询。

这是一个相当复杂的例子,虽然我不确定它会有多大帮助。

ALTER PROCEDURE [dbo].[CMS_GetCollection]
    @sectionContentTemplateID int
    ,@count int = null
AS
BEGIN
    SET NOCOUNT ON;
SET NOCOUNT ON;

    declare @columns varchar(max)
    select 
        @columns = COALESCE(@columns + ',[' + cte.name + '/' + a.name + ']', '[' + cte.name + '/' + a.name + ']')
    from tcmssection_contenttemplate sct 
                inner join tcmssection s on s.sectionid = sct.sectionid
                inner join tcmscontenttemplate ct on ct.contenttemplateid = sct.contenttemplateid
                inner join tcmscontenttemplate_element cte on cte.contenttemplateid = ct.contenttemplateid
                inner join tcmselement e on cte.elementid = e.elementid
                inner join tcmsattribute a on e.elementid = a.elementid 
    where 
        a.isadmin = 0
        and sct.sectioncontenttemplateid = @sectionContentTemplateID

    declare @query varchar(max)
    set @query = 
    'select ' + case when @count is not null then 
    ' top ' + convert(varchar(10),@count) else '' end + '
        [url]
        ,pageId,pageName,sortOrder
        ,[date]
        , ' + @columns + '
    from (
        select 
            s.domainpath + p.filename as [url]
            ,cte.name + ''/'' + a.name as [element.attribute]
            ,isnull(pva.valuevarchar, isnull(pva.valuetext,'''')) as [value]
            ,pv.datepublished as [date],isnull(p.sortOrder,0) as sortOrder,p.pageID,p.name as pageName
          from tcmssection_contenttemplate sct 
            inner join tcmssection s on s.sectionid = sct.sectionid
            inner join tcmspage p on p.sectioncontenttemplateid = sct.sectioncontenttemplateid
            inner join tcmscontenttemplate ct on ct.contenttemplateid = sct.contenttemplateid
            inner join tcmscontenttemplate_element cte on cte.contenttemplateid = ct.contenttemplateid
            inner join tcmselement e on cte.elementid = e.elementid
            inner join tCMSPageVersion pv on p.pageID = pv.pageID
                and pv.pageVersionID = (
                    select top 1 pageVersionID 
                    from tCMSPageVersion 
                    where pageID = p.pageID and datePublished is not null and datecancelled is null 
                    order by datePublished desc
                )
            inner join tcmsattribute a on e.elementid = a.elementid
            left outer join tcmspageversion_element pve on pve.pageversionid = pv.pageversionid 
                and pve.elementid = e.elementid and pve.name = cte.name
            left outer join tcmspageversion_attribute pva on pva.attributeid = a.attributeid 
                and pve.pageversionelementid = pva.pageversionelementid
        where 
            p.isDeleted = 0
            and a.isadmin = 0
            and sct.sectioncontenttemplateid = ' + convert(varchar(10), @sectionContentTemplateID) + '

    ) [data]
    pivot (
        max([value])
        for [element.attribute] 
        in ( ' + @columns +  ' )
    ) as [pivot]    
    order by [date] desc'

    execute(@query)

END

答案 2 :(得分:1)

这适用于一定数量的列。如果您需要的列数将发生变化,您还可以使用动态SQL来提供类似的内容。

SELECT
  products.pname,
  MAX(CASE WHEN rules.rname = 'Rule 0' THEN products.pid * rules.rid) AS pid_r0,
  MAX(CASE WHEN rules.rname = 'Rule 1' THEN products.pid * rules.rid) AS pid_r1,
  MAX(CASE WHEN rules.rname = 'Rule 2 'THEN products.pid * rules.rid) AS pid_r2,
  ...
FROM products
CROSS JOIN rules
GROUP BY products.pname;

答案 3 :(得分:0)

老实说,我不得不暂时处理内部数据处理问题(与SQL行的最大长度为24000字节左右)。我的解决方案是使用XML数据类型,因为它使您能够尽可能轻松地完成大多数这些随机类型的数据转换。其他作者提出的Pivot问题是Pivots依赖聚合函数来成功 - 这是我从未想过的事情。