0

我有一个结果集,我想通过Service列“展平”。示例将最好地解释它:

给定这个结果集(让我们称之为request):

---------------------------------------
| Id |    Service   |  C1 | ... | Cn  |
--------------------------------------|
|  1 |       A      |  5  |     | 3   |
--------------------------------------|
|  1 |       B      |  2  |     | 1   |
--------------------------------------|
|  2 |       A      |  9  |     | 4   |
--------------------------------------

我想得到这个:

---------------------------------------------------------------------------
| Id |  ServiceA_C1 | ... | ServiceA_Cn |ServiceB_C1 | ... | ServiceB_C2n |
---------------------------------------------------------------------------
| 1 |       5       | ... |      3      |     2      | ... |       1      |   
---------------------------------------------------------------------------
| 2 |       9       | ... |      4      |    NULL    | ... |      NULL    |   
---------------------------------------------------------------------------

最终期望的结果:

  • 每个 Id(有多个值现在只有一行)
  • 服务的每个不同值将n在最终结果集中具有列

当前解决方案(有效,但超长且效率不高):

SELECT A.C1 AS ServiceA_C1, ..,A.Cn AS ServiceA_Cn,B.C1 AS ServiceB_C1, ..,B.Cn AS ServiceB_Cn  
FROM (SELECT *
      FROM request
      WHERE Service = 'A') AS A
    JOIN
      (SELECT *
      FROM request
      WHERE Service = 'B') AS B
    ON A.Id = B.Id

注:
服务数量约为 10(#distinct 值Service列中的#distinct 值),这是问题本身的通用措辞。

我知道 SQL 操作,如枢轴、反枢轴、交叉应用、连接等,但这个问题仍然让我烦恼,因为我没有找到立即解决这个问题的棘手问题。我很高兴知道其中一种方法可以解决这个问题,但我错过了。

谢谢

4

1 回答 1

2

您可以使用 unpivot/pivot 来获得您想要的结果。有几种不同的方法可以获得结果,如果值数量有限,则可以对查询进行硬编码,但如果值数量未知,则需要使用动态 SQL。

UNPIVOT 进程会将c1, etc` 的多列转换为多行。一旦数据位于多行中,您就可以轻松应用 PIVOT 函数。您可以使用 unpivot 函数或 CROSS APPLY 来转换来自多个列的数据:

select id,
  col = 'Service'+Service+'_'+col+'_'+cast(seq as varchar(10)),
  value
from
(
  select id, service, c1, cn
    , row_number() over(partition by id
                        order by service) seq
  from yourtable
) t
cross apply
(
  select 'c1', c1 union all
  select 'cn', cn
) c (col, value)

请参阅SQL Fiddle with Demo。交叉应用会将您的数据转换为以下格式:

| ID |           COL | VALUE |
|  1 | ServiceA_c1_1 |     5 |
|  1 | ServiceA_cn_1 |     3 |
|  1 | ServiceB_c1_2 |     2 |
|  1 | ServiceB_cn_2 |     1 |
|  2 | ServiceA_c1_1 |     9 |
|  2 | ServiceA_cn_1 |     4 |

一旦数据采用这种格式,您就可以应用 PIVOT:

select id, ServiceA_c1_1, ServiceA_cn_1,
  ServiceB_c1_2, ServiceB_cn_2
from
(
  select id,
    col = 'Service'+Service+'_'+col+'_'+cast(seq as varchar(10)),
    value
  from
  (
    select id, service, c1, cn
      , row_number() over(partition by id
                          order by service) seq
    from yourtable
  ) t
  cross apply
  (
    select 'c1', c1 union all
    select 'cn', cn
  ) c (col, value)
) d
pivot
(
  max(value)
  for col in (ServiceA_c1_1, ServiceA_cn_1,
              ServiceB_c1_2, ServiceB_cn_2)
) piv;

请参阅SQL Fiddle with Demo

然后如果你有未知数量的值,你可以将上面的查询转换为动态 SQL:

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

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

set @query = 'SELECT id, ' + @cols + ' 
            from 
            (
              select id,
                col = ''Service''+Service+''_''+col+''_''+cast(seq as varchar(10)),
                value
              from
              (
                select id, service, c1, cn
                  , row_number() over(partition by id
                                      order by service) seq
                from yourtable
              ) t
              cross apply
              (
                select ''c1'', c1 union all
                select ''cn'', cn
              ) c (col, value)
            ) x
            pivot 
            (
                max(value)
                for col in (' + @cols + ')
            ) p '

execute sp_executesql @query;

请参阅SQL Fiddle with Demo。两者都会给出结果:

| ID | SERVICEA_C1_1 | SERVICEA_CN_1 | SERVICEB_C1_2 | SERVICEB_CN_2 |
|  1 |             5 |             3 |             2 |             1 |
|  2 |             9 |             4 |        (null) |        (null) |
于 2013-10-02T19:12:15.597 回答