将行转换为具有基于排名

时间:2017-09-18 14:31:43

标签: sql sql-server

我有一个客户表,其中有几种"帐户",每个都有不同的整数余额,例如

CUSTID     AcctType    Balance
12345      Checking    1000
12345      Savings     5000
12345      Investment  3000 
98765      Savings     2000
98765      Checking    8000
98765      Investment  1000 
98765      Retirement  2500

我不知道客户可能拥有多少帐户(可能是1到6之间的任何帐户)。我必须按照从最高到最低的顺序创建一个显示“帐户和余额”列的结果,如下所示:

CUSTID     AcctType1    Balance1   AcctType2   Balance2  AcctType3   Balance3  AcctType4   Balance 4
12345      Savings      5000       Investment  3000      Checking    1000
98765      Checking     8000       Retirement  2500      Savings     2000      Investment   1000

如何在SQL Server中创建它? (理想情况下作为观点)

2 个答案:

答案 0 :(得分:2)

您可以先将unpivot列添加到行中,然后pivot将行返回到包含行号的列中,如下所示:

WITH CTE
AS
(
  SELECT 
     CAST(CUSTID AS NVARCHAR(50)) AS CUSTID
     ,CAST(AcctType AS NVARCHAR(50)) AS AcctType
     ,CAST(Balance AS NVARCHAR(50)) AS Balance
    ,ROW_NUMBER() OVER(PARTITION BY [CUSTID] ORDER BY Balance DESC) AS RN
  FROM Data
), unpivoted
AS
(
  SELECT CUSTID, val, col + ' ' + CAST(RN AS NVARCHAR(50)) AS col
  FROM CTE 
  UNPIVOT
  (
    val
    FOR col IN(AcctType, Balance)
  ) AS u
)
SELECT *
FROM unpivoted AS u
PIVOT
(
  MAX(val)
  FOR col IN([AcctType 1], [Balance 1], 
             [AcctType 2], [Balance 2],
             [AcctType 3], [Balance 3],
             [AcctType 4], [Balance 4])
) AS p;

SQL Fiddle Demo

<强>更新

如果您想为任意数量的客户提供动态,您必须动态执行此操作:

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

WITH CTE
AS
(
  SELECT 
     CAST(CUSTID AS NVARCHAR(50)) AS CUSTID
     ,CAST(AcctType AS NVARCHAR(50)) AS AcctType
     ,CAST(Balance AS NVARCHAR(50)) AS Balance
    ,ROW_NUMBER() OVER(PARTITION BY [CUSTID] ORDER BY Balance DESC) AS RN
  FROM Data
), Data
AS
(
  SELECT col, MAX(RN) AS RN
  FROM
  (
    SELECT RN, col + CAST(RN AS NVARCHAR(50)) AS col
    FROM CTE 
    UNPIVOT
    (
      val
      FOR col IN(AcctType, Balance)
    ) AS u
  ) AS t
  GROUP BY col
)
select @cols = STUFF((SELECT ',' +
                        QUOTENAME(col)
                      FROM Data
                      ORDER BY RN
                      FOR XML PATH(''), TYPE
                     ).value('.', 'NVARCHAR(MAX)') 
                        , 1, 1, '');

SELECT @query = 'WITH CTE
AS
(
  SELECT 
     CAST(CUSTID AS NVARCHAR(50)) AS CUSTID
     ,CAST(AcctType AS NVARCHAR(50)) AS AcctType
     ,CAST(Balance AS NVARCHAR(50)) AS Balance
    ,ROW_NUMBER() OVER(PARTITION BY [CUSTID] ORDER BY Balance DESC) AS RN
  FROM Data
), unpivoted
AS
(
  SELECT CUSTID, val, col + CAST(RN AS NVARCHAR(50)) AS col
  FROM CTE 
  UNPIVOT
  (
    val
    FOR col IN(AcctType, Balance)
  ) AS u
)
SELECT *
FROM unpivoted AS u
PIVOT
(
  MAX(val)
  FOR col IN('+ @cols + ')
) AS p;';


EXECUTE(@query);

Dynamic Demo

答案 1 :(得分:1)

如果列数有限(正如您所说的那样,每CUSTID只有6个),那么您可以使用ROW_NUMBER()条件聚合:

SELECT t.custID,
       MAX(CASE WHEN t.rnk = 1 THEN t.accttype END) as accttype1,
       MAX(CASE WHEN t.rnk = 1 THEN t.balance END) as balance1,
       MAX(CASE WHEN t.rnk = 2 THEN t.accttype END) as accttype2,
       MAX(CASE WHEN t.rnk = 2 THEN t.balance END) as balance2,
       MAX(CASE WHEN t.rnk = 3 THEN t.accttype END) as accttype3,
       MAX(CASE WHEN t.rnk = 3 THEN t.balance END) as balance3,
       MAX(CASE WHEN t.rnk = 4 THEN t.accttype END) as accttype4,
       MAX(CASE WHEN t.rnk = 4 THEN t.balance END) as balance4,
       MAX(CASE WHEN t.rnk = 5 THEN t.accttype END) as accttype5,
       MAX(CASE WHEN t.rnk = 5 THEN t.balance END) as balance5,
       MAX(CASE WHEN t.rnk = 6 THEN t.accttype END) as accttype6,
       MAX(CASE WHEN t.rnk = 6 THEN t.balance END) as balance6
FROM (SELECT s.*,
             ROW_NUMBER() OVER(PARTITION BY s.custID ORDER BY s.balance DESC) as rnk
      FROM YourTable s) t
GROUP BY t.custID