SQL 结合 2 table 和枢轴

SQL combine 2 table and pivot

我不明白 PIVOT 在 SQL 中是如何工作的。我有 2 个 table,我想旋转其中的 1 个,以便只得到 1 个 table 的所有数据。我附上了一张图片,其中包含我拥有的 table 和我想要得到的结果。

CREATE TABLE TABLE1
    ([serie_id] varchar(4), [Maturity] int, [Strategy] int, [Lifetime] varchar(4), [L_max] decimal(10, 5), [W_max] decimal(10, 5), [H_max] decimal(10, 5))
;

INSERT INTO TABLE1
    ([serie_id], [Maturity], [Strategy], [Lifetime], [L_max], [W_max], [H_max])
VALUES
    ('id_1', 3, 1, '2', 2.200, 1.400, 1.400),
    ('id_2', 3, 1, '2', 3.400, 1.800, 2.100),
    ('id_3', 3, 1, NULL, 24.500, 14.500, 15.000),
    ('id_4', 3, 1, NULL, 28.000, 24.500, 14.000)
;

CREATE TABLE TABLE2
    ([serie_id] varchar(4), [L_value] decimal(10, 5), [lrms] decimal(10, 5), [latTmax] decimal(10, 5), [Rdc] decimal(10, 5))
;

INSERT INTO TABLE2
    ([serie_id], [L_value], [lrms], [latTmax], [Rdc])
VALUES
    ('id_1', 67.000, 400.000, 400.000, 0.250),
    ('id_1', 90.000, 330.000, 330.000, 0.350),
    ('id_1', 120.000, 370.000, 370.000, 0.300),
    ('id_1', 180.000, 330.000, 300.000, 0.350),
    ('id_2', 260.000, 300.000, 300.000, 0.400),
    ('id_2', 360.000, 280.000, 280.000, 0.450),
    ('id_3', 90.000, 370.000, 370.000, 0.300),
    ('id_4', 160.000, 340.000, 340.000, 0.400)
;

SQLFiddle

如果有人可以帮助我解决 SQL 查询,我将不胜感激。

为了获得最终结果,您将不得不实施各种方法,包括 unpivot、pivot,以及 row_number() 等窗口函数的使用。

由于 Table2 中有多列需要旋转,因此您需要先取消旋转它们。这是数据透视表的反面,它将您的多列转换为多行。但是在逆透视之前,您需要一些值来使用 row_number() 来标识每一行的值 - 听起来很复杂,对吧?

首先,使用窗口函数 row_number() 查询 table2。这会为每一行创建一个唯一标识符,并允许您轻松地将 id_1 的值与其他任何值相关联。

select serie_id, l_value, lrms, latTmax, Rdc,
    rn = cast(row_number() over(partition by serie_id order by serie_id)
              as varchar(10))
from table2;

参见 Demo。创建此唯一标识符后,您将 unpivot L_valuelrmslatTmaxrdc。您可以使用几种不同的方法对数据进行逆透视,包括逆透视函数、CROSS APPLY 或 UNION ALL。

select serie_id,
  col, value
from
(
  select serie_id, l_value, lrms, latTmax, Rdc,
    rn = cast(row_number() over(partition by serie_id order by serie_id)
              as varchar(10))
  from table2 
) d
cross apply 
(
  select 'L_value_'+rn, L_value union all
  select 'lrms_'+rn, lrms union all
  select 'latTmax_'+rn, latTmax union all
  select 'Rdc_'+rn, Rdc
) c (col, value)

参见 SQL Fiddle with Demotable2 中的数据不是完全不同的格式,可以转入新列:

| SERIE_ID |       COL | VALUE |
|----------|-----------|-------|
|     id_1 | L_value_1 |    67 |
|     id_1 |    lrms_1 |   400 |
|     id_1 | latTmax_1 |   400 |
|     id_1 |     Rdc_1 |  0.25 |
|     id_1 | L_value_2 |    90 |
|     id_1 |    lrms_2 |   330 |
|     id_1 | latTmax_2 |   330 |
|     id_1 |     Rdc_2 |  0.35 |

最后一步是将上述数据 PIVOT 到最终结果中:

select serie_id, maturity, strategy, lifetime, l_max, w_max, h_max,
  L_value_1, lrms_1, latTmax_1, Rdc_1,
  L_value_2, lrms_2, latTmax_2, Rdc_2,
  L_value_3, lrms_3, latTmax_3, Rdc_3,
  L_value_4, lrms_4, latTmax_4, Rdc_4
from
(
  select t1.serie_id, t1.maturity, t1.strategy, t1.lifetime,
    t1.l_max, t1.w_max, t1.h_max,
    t2.col, t2.value
  from table1 t1
  inner join
  (
    select serie_id,
      col, value
    from
    (
      select serie_id, l_value, lrms, latTmax, Rdc,
        rn = cast(row_number() over(partition by serie_id order by serie_id)
                  as varchar(10))
      from table2 
    ) d
    cross apply 
    (
      select 'L_value_'+rn, L_value union all
      select 'lrms_'+rn, lrms union all
      select 'latTmax_'+rn, latTmax union all
      select 'Rdc_'+rn, Rdc
    ) c (col, value)
  ) t2
    on t1.serie_id = t2.serie_id
) d
pivot
(
  max(value)
  for col in (L_value_1, lrms_1, latTmax_1, Rdc_1,
              L_value_2, lrms_2, latTmax_2, Rdc_2,
              L_value_3, lrms_3, latTmax_3, Rdc_3,
              L_value_4, lrms_4, latTmax_4, Rdc_4)
) p;

参见 SQL Fiddle with Demo

如果您在 Table2 中有未知数量的值,那么您将需要使用动态 SQL 创建将要执行的 sql 字符串。一旦逻辑正确,将上述代码转换为动态 sql 就非常容易了。代码将是:

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

select @cols 
    = STUFF((SELECT ',' + QUOTENAME(col+cast(rn as varchar(10))) 
             from 
             (
               select rn = cast(row_number() over(partition by serie_id order by serie_id)
                  as varchar(10))
               from table2
             ) d
             cross apply
             (
               select 'L_value_', 0 union all
                select 'lrms_', 1 union all
                select 'latTmax_', 2 union all
                select 'Rdc_', 3
             ) c (col, so)
             group by col, rn, so
             order by rn, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query = N'SELECT serie_id, maturity, strategy, lifetime, l_max, 
                w_max, h_max,' + @cols + N' 
              from 
             (
               select t1.serie_id, t1.maturity, t1.strategy, t1.lifetime,
                t1.l_max, t1.w_max, t1.h_max,
                t2.col, t2.value
              from table1 t1
              inner join
              (
                select serie_id,
                  col, value
                from
                (
                  select serie_id, l_value, lrms, latTmax, Rdc,
                    rn = cast(row_number() over(partition by serie_id order by serie_id)
                              as varchar(10))
                  from table2 
                ) d
                cross apply 
                (
                  select ''L_value_''+rn, L_value union all
                  select ''lrms_''+rn, lrms union all
                  select ''latTmax_''+rn, latTmax union all
                  select ''Rdc_''+rn, Rdc
                ) c (col, value)
              ) t2
                on t1.serie_id = t2.serie_id
            ) x
            pivot 
            (
                max(value)
                for col in (' + @cols + N')
            ) p '

exec sp_executesql @query

SQL Fiddle with Demo

两个版本都会给出以下结果:

| SERIE_ID | MATURITY | STRATEGY | LIFETIME | L_MAX | W_MAX | H_MAX | L_VALUE_1 | LRMS_1 | LATTMAX_1 | RDC_1 | L_VALUE_2 | LRMS_2 | LATTMAX_2 |  RDC_2 | L_VALUE_3 | LRMS_3 | LATTMAX_3 |  RDC_3 | L_VALUE_4 | LRMS_4 | LATTMAX_4 |  RDC_4 |
|----------|----------|----------|----------|-------|-------|-------|-----------|--------|-----------|-------|-----------|--------|-----------|--------|-----------|--------|-----------|--------|-----------|--------|-----------|--------|
|     id_1 |        3 |        1 |        2 |   2.2 |   1.4 |   1.4 |        67 |    400 |       400 |  0.25 |        90 |    330 |       330 |   0.35 |       120 |    370 |       370 |    0.3 |       180 |    330 |       300 |   0.35 |
|     id_2 |        3 |        1 |        2 |   3.4 |   1.8 |   2.1 |       260 |    300 |       300 |   0.4 |       360 |    280 |       280 |   0.45 |    (null) | (null) |    (null) | (null) |    (null) | (null) |    (null) | (null) |
|     id_3 |        3 |        1 |   (null) |  24.5 |  14.5 |    15 |        90 |    370 |       370 |   0.3 |    (null) | (null) |    (null) | (null) |    (null) | (null) |    (null) | (null) |    (null) | (null) |    (null) | (null) |
|     id_4 |        3 |        1 |   (null) |    28 |  24.5 |    14 |       160 |    340 |       340 |   0.4 |    (null) | (null) |    (null) | (null) |    (null) | (null) |    (null) | (null) |    (null) | (null) |    (null) | (null) |