简体   繁体   English

如何在SQL Server中透视未知的列数且没有聚合?

[英]How to pivot unknown number of columns & no aggregate in SQL Server?

I have query which returns clients loans with associated collateral names like below (1) but I want to have only one distinct loan number in a row and collateral names aside like on other example (2). 我有一个查询,该查询返回具有关联抵押名称的客户贷款,如以下(1)所示,但我想连续仅拥有一个不同的贷款编号,并且像其他示例(2)那样保留抵押名称。 Been playing with pivoting but cannot figure it out because I don't have aggregate column and I don't know how many loan numbers I will get neither how many collateral each loan may have. 一直在进行透视,但由于我没有汇总列,也无法弄清楚,我也不知道每笔贷款可能没有多少抵押品。 How to do that??? 怎么做??? Possible in SQL Server 2012? 在SQL Server 2012中可能吗?

thanks 谢谢

(1) (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) I need something like this (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|

While M.Ali's answer will get you the result, since you are using SQL Server 2012 I would unpivot the name and address columns slightly different to get the final result. 虽然M.Ali的答案将为您提供结果,但是由于您使用的是SQL Server 2012,因此我将取消对nameaddress列的略有不同以得出最终结果。

Since you are using SQL Server 2012, you can use CROSS APPLY with VALUES to unpivot these multiple columns into multiple rows. 由于您使用的是SQL Server 2012,因此可以将CROSS APPLYVALUES使用,以将这些多列取消透视成多行。 But before you do that, I would use row_number() to get the total number of new columns you will have. 但是在您这样做之前,我将使用row_number()来获取您将拥有的新列的总数。

The code to "UNPIVOT" the data using CROSS APPLY looks like: 使用CROSS APPLY来“ UNPIVOT”数据的代码如下:

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);

See SQL Fiddle with Demo . 请参阅带有演示的SQL Fiddle This is going to get your data into a format similar to: 这将使您的数据变为类似于以下格式:

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

You now have a single column COL with all of your new column names and the values associated are also in a single column. 现在,您将拥有一个包含所有新列名的单列COL ,并且关联的值也位于一个列中。 The new column names now have a number at the end (1, 2, 3, etc) based on how many total entries you have per loanid . 现在,新列名的末尾有一个数字(1、2、3等),具体取决于每个loanid条目loanid Now you can apply PIVOT: 现在您可以申请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;

See SQL Fiddle with Demo . 请参阅带有演示的SQL Fiddle Finally if you don't know how many pairs of Name and Address you will have then you can use dynamic SQL: 最后,如果您不知道将有多少对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;

See SQL Fiddle with Demo . 请参阅带有演示的SQL Fiddle Both versions give a result: 这两个版本都给出结果:

| 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) |

Test Data 测试数据

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')

Query 询问

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

Result Set 结果集

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

Update for Dynamic Columns 动态列更新

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

Note 注意

This wouldnt work with the given sample data in my answer, as it uses a table variable and it is not visible to dynamic sql since it has it own scope. 这将无法与我的答案中给定的示例数据一起使用,因为它使用表变量,并且由于它具有自己的作用域,因此对于动态sql不可见。 but this solution will work on a normal sql server table. 但是此解决方案可以在普通的sql server表上使用。

Also the order in which columns are selected will be slightly different. 同样,选择列的顺序也会略有不同。

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

This would put 这将把

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM