【问题标题】:How to pivot unknown number of columns & no aggregate in SQL Server?如何在 SQL Server 中旋转未知数量的列和非聚合?
【发布时间】:2014-03-31 21:25:25
【问题描述】:

我有一个查询,它返回带有关联抵押品名称的客户贷款,如下 (1) 但我只想在一行中只有一个不同的贷款编号和抵押品名称,就像在其他示例 (2) 中一样。一直在玩旋转但无法弄清楚,因为我没有聚合列,而且我不知道有多少贷款数量,我也不会得到每笔贷款可能有多少抵押品。怎么做???可以在 SQL Server 2012 中使用吗?

谢谢

(1)

loanid|name  |Address |
1     |John  |New York|
1     |Carl  |New York|
1     |Henry |Boston  |
2     |Robert|Chicago |
3     |Joanne|LA      |
3     |Chris |LA      |

(2) 我需要这样的东西

loanid|name  |address  |name |address |name|address|
1     |Jonh  |New York |Carl |New York|Henry|Boston|
2     |Robert|Chicago  |
3     |Joanne|LA       |Chris|LA|

【问题讨论】:

  • 您的每个name/addresss 是否需要单独的列,它们可以是一列中的逗号分隔列表吗?您还使用什么 RDBMS?
  • 布拉德,我已经提到过 - SQL Server 2012。名称/地址可以用逗号分隔。

标签: sql-server sql-server-2012 pivot


【解决方案1】:

虽然M.Ali's answer 将为您提供结果,但由于您使用的是 SQL Server 2012,因此我将取消旋转 nameaddress 列略有不同以获得最终结果。

由于您使用的是 SQL Server 2012,因此您可以使用 CROSS APPLYVALUES 将这些多列取消透视为多行。但在您这样做之前,我会使用row_number() 来获取您将拥有的新列的总数。

使用 CROSS APPLY 来“取消透视”数据的代码如下所示:

select d.loanid, 
  col = c.col + cast(seq as varchar(10)),
  c.value
from
(
  select loanid, name, address,
    row_number() over(partition by loanid
                      order by loanid) seq
  from yourtable
) d
cross apply
(
  values
    ('name', name),
    ('address', address)
) c(col, value);

SQL Fiddle with Demo。这将使您的数据格式类似于:

| LOANID |      COL |    VALUE |
|--------|----------|----------|
|      1 |    name1 |     John |
|      1 | address1 | New York |
|      1 |    name2 |     Carl |
|      1 | address2 | New York |
|      1 |    name3 |    Henry |
|      1 | address3 |   Boston |

您现在拥有一列 COL,其中包含所有新列名称,并且关联的值也位于一列中。现在,新列名称的末尾有一个数字(1、2、3 等),具体取决于每个 loanid 的总条目数。现在您可以申请 PIVOT:

select loanid,
  name1, address1, name2, address2,
  name3, address3
from
(
  select d.loanid, 
    col = c.col + cast(seq as varchar(10)),
    c.value
  from
  (
    select loanid, name, address,
      row_number() over(partition by loanid
                        order by loanid) seq
    from yourtable
  ) d
  cross apply
  (
    values
      ('name', name),
      ('address', address)
  ) c(col, value)
) src
pivot
(
  max(value)
  for col in (name1, address1, name2, address2,
              name3, address3)
) piv;

SQL Fiddle with Demo。最后,如果您不知道您将拥有多少对 NameAddress,那么您可以使用动态 SQL:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(col+cast(seq as varchar(10))) 
                    from 
                    (
                      select row_number() over(partition by loanid
                                                order by loanid) seq
                      from yourtable
                    ) d
                    cross apply
                    (
                      select 'Name', 1 union all
                      select 'Address', 2
                    ) c (col, so)
                    group by seq, col, so
                    order by seq, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT loanid,' + @cols + ' 
            from 
            (
              select d.loanid, 
                col = c.col + cast(seq as varchar(10)),
                c.value
              from
              (
                select loanid, name, address,
                  row_number() over(partition by loanid
                                    order by loanid) seq
                from yourtable
              ) d
              cross apply
              (
                values
                  (''name'', name),
                  (''address'', address)
              ) c(col, value)
            ) x
            pivot 
            (
                max(value)
                for col in (' + @cols + ')
            ) p '

exec sp_executesql @query;

SQL Fiddle with Demo。两个版本都给出了结果:

| LOANID |  NAME1 | ADDRESS1 |  NAME2 | ADDRESS2 |  NAME3 | ADDRESS3 |
|--------|--------|----------|--------|----------|--------|----------|
|      1 |   John | New York |   Carl | New York |  Henry |   Boston |
|      2 | Robert |  Chicago | (null) |   (null) | (null) |   (null) |
|      3 | Joanne |       LA |  Chris |       LA | (null) |   (null) |

【讨论】:

  • 谢谢,但它看起来太复杂了。不过会检查这些交叉申请。
  • @ZuluZ 两个问题。 1. 太复杂了怎么办?我假设这是 TLDR 的缩写,这很可悲。 2:有人已经为您完成了所有工作,以及关于为什么会这样工作的一些非常详细的解释,这有多复杂。
【解决方案2】:

测试数据

DECLARE @TABLE TABLE (loanid INT,name VARCHAR(20),[Address] VARCHAR(20))
INSERT INTO @TABLE VALUES
(1,'John','New York'),(1,'Carl','New York'),(1,'Henry','Boston'),
(2,'Robert','Chicago'),(3,'Joanne','LA'),(3,'Chris','LA')

查询

SELECT  loanid
       ,ISNULL(name1, '')    AS name1
       ,ISNULL(Address1, '') AS Address1
       ,ISNULL(name2, '')    AS name2
       ,ISNULL(Address2, '') AS Address2
       ,ISNULL(name3, '')    AS name3
       ,ISNULL(Address3, '') AS Address3
FROM (
SELECT loanid
      ,'name' + CAST(ROW_NUMBER() OVER (PARTITION BY loanid ORDER BY loanid)  AS NVARCHAR(10)) AS Cols
      , name AS Vals
FROM @TABLE
UNION ALL
SELECT loanid
      ,'Address' + CAST(ROW_NUMBER() OVER (PARTITION BY loanid ORDER BY loanid)  AS NVARCHAR(10)) 
      , [Address] 
FROM @TABLE ) t
 PIVOT (MAX(Vals)
        FOR Cols 
        IN (name1, Address1,name2,Address2,name3,Address3)
        )P

结果集

╔════════╦════════╦══════════╦═══════╦══════════╦═══════╦══════════╗
║ loanid ║ name1  ║ Address1 ║ name2 ║ Address2 ║ name3 ║ Address3 ║
╠════════╬════════╬══════════╬═══════╬══════════╬═══════╬══════════╣
║      1 ║ John   ║ New York ║ Carl  ║ New York ║ Henry ║ Boston   ║
║      2 ║ Robert ║ Chicago  ║       ║          ║       ║          ║
║      3 ║ Joanne ║ LA       ║ Chris ║ LA       ║       ║          ║
╚════════╩════════╩══════════╩═══════╩══════════╩═══════╩══════════╝

动态列更新

DECLARE @Cols NVARCHAR(MAX);

SELECT @Cols =  STUFF((
                    SELECT DISTINCT ', ' +  QUOTENAME(Cols)
                    FROM (
                    SELECT loanid
                          ,'name' + CAST(ROW_NUMBER() OVER (PARTITION BY loanid ORDER BY loanid)  AS NVARCHAR(10)) AS Cols
                          , name AS Vals
                    FROM @TABLE
                    UNION ALL
                    SELECT loanid
                          ,'Address' + CAST(ROW_NUMBER() OVER (PARTITION BY loanid ORDER BY loanid)  AS NVARCHAR(10)) 
                          , [Address] 
                    FROM @TABLE ) t
                    GROUP BY QUOTENAME(Cols)
                    FOR XML PATH(''), TYPE).value('.','NVARCHAR(MAX)'),1,2,'')


DECLARE @Sql NVARCHAR(MAX);

SET @Sql  = 'SELECT ' + @Cols +   '
            FROM (
            SELECT loanid
                  ,''name'' + CAST(ROW_NUMBER() OVER 
                            (PARTITION BY loanid ORDER BY loanid)  AS NVARCHAR(10)) AS Cols
                  , name AS Vals
            FROM @TABLE
            UNION ALL
            SELECT loanid
                  ,''Address'' + CAST(ROW_NUMBER() OVER 
                                (PARTITION BY loanid ORDER BY loanid)  AS NVARCHAR(10)) 
                  , [Address] 
            FROM @TABLE ) t
             PIVOT (MAX(Vals)
                    FOR Cols 
                    IN (' + @Cols + ')
                    )P'

EXECUTE sp_executesql @Sql

注意

这不适用于我的答案中给定的示例数据,因为它使用表变量并且它对动态 sql 不可见,因为它有自己的范围。但此解决方案适用于普通的 sql server 表。

此外,选择列的顺序也会略有不同。

【讨论】:

  • 这适用于 3 列,但必须动态构建才能真正对“未知数量的列”有用
  • 那也没那么难,让我帮你把它拿出来:)
  • @Brad 你不得不问我这不是你,现在看看朋友第二个解决方案是动态列数。
  • +1 非常好的答案。这个对我有用! (虽然不是我的问题:))
  • 如果您真的想学习一些很酷的枢轴技术,请查找名为 Bluefeet 的用户,查看该用户对问题的回答,您会学到很多东西。 :)
【解决方案3】:
SELECT DISTINCT
       loanid
      ,STUFF((SELECT DISTINCT ',' + name +' ('+address+')'
              FROM table a 
              WHERE a.loanid = b.loanid
              FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'),1,1,'')

FROM table  b

这会让

loanid | name(address)
1      | name (address),name2 (address2),name3........
2      | name (address),name2 (address2),name3........
3      | name (address),name2 (address2),name3........

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多