将行值转换为列名

时间:2020-10-07 15:57:47

标签: sql sql-server pivot

我有一张客户联系人及其角色的表格。下面是简化的示例。

customer | role        | userid
----------------------------
1        | Support     | 123
1        | Support     | 456
1        | Procurement | 567
...

所需的输出

customer | Support1 | Support2 | Support3 | Support4 | Procurement1 | Procurement2
-----------------------------------------------------------------------------------
1        | 123      | 456      | null     | null     | 567          | null
2        | 123      | 456      | 12333    | 45776    | 888          | 56723

因此,根据该角色中的用户数量动态创建所需列的数量。这是少数角色。同样,我可以假设最多5个用户担任同一角色。这意味着最坏的情况是我需要为每个角色生成5列。用户ID不必按任何特定顺序。

我当前的方法是每个角色/客户获得1个用户ID。然后第二个查询提取另一个不属于第一个结果集的ID。等等。但是那样,我必须静态创建5个查询。有用。但是我想知道是否有更有效的方法?动态创建所需的列。

每个角色吸引一位用户的示例:

SELECT customer,role,
(SELECT  top 1 userid
FROM temp as tmp1
where tmp1.customer=tmp2.customer and tmp1.role=tmp2.role
) as userid
 FROM temp as tmp2
 group by customer,role
 order by customer,role

使用伪数据创建SQL

create table temp
    (
      customer int,
      role nvarchar(20),
      userid int
    )
    
    insert into temp values (1,'Support',123)
    insert into temp values (1,'Support',456)
    insert into temp values (1,'Procurement',567)
    insert into temp values (2,'Support',123)
    insert into temp values (2,'Support',456)
    insert into temp values (2,'Procurement',888)
    insert into temp values (2,'Support',12333)
    insert into temp values (2,'Support',45776)
    insert into temp values (2,'Procurement',56723)

1 个答案:

答案 0 :(得分:0)

如果要避免进入对用户定义的表函数进行编程的领域(这是动态生成列所需要的),则可能需要稍微调整一下方法。您没有提到正在使用哪个SQL数据库变体(SQL Server,PostgreSQL 、?)。我将假设它支持某种形式的字符串聚合功能(它们几乎都支持),但是这样做的语法会有所不同,因此您可能必须根据情况调整代码。您提到角色数量很少(5英镑?)。提议的解决方案是使用公用表表达式(CTE)和LISTAGG(在其他数据库中分别命名为STRING_AGG,GROUP_CONCAT等)函数来生成用逗号分隔的用户ID列表,每个角色一个用户ID。

WITH tsupport 
     AS (SELECT customer, 
                Listagg(userid, ',') AS "Support" 
         FROM   temp 
         WHERE  ROLE = 'Support' 
         GROUP  BY customer), 
     tprocurement 
     AS (SELECT customer, 
                Listagg(userid, ',') AS "Procurement" 
         FROM   temp 
         WHERE  ROLE = 'Procurement' 
         GROUP  BY customer)
 --> tnextrole... 
 --> AS (SELECT ... for additional roles 
 -->            Listagg...
SELECT a.customer, 
       "Support", 
       "Procurement" 
 -->   "Next Role"  etc.
FROM   tsupport a 
       JOIN tprocurement b 
         ON a.customer = b.customer 
 -->   JOIN tNextRole ...

提琴是here,根据您的虚拟数据,结果如下所示:

Output based on your sample