【问题标题】:PIVOT Operator on two columns两列上的 PIVOT 运算符
【发布时间】:2020-09-30 07:21:15
【问题描述】:

我对 SQL 很陌生,更不用说在 SSMS 中做 PIVOTS。在做了一些研究之后,我很难为两列做枢轴。

我查询时的数据本身是:

SELECT
   INSTRUMENT_ID,
   VALUE_TYPE_CODE,
   ORIGINAL_TRANSACTION_VALUE,
   VALUE_IN_HOME_CURRENCY

FROM
   FACT_TABLE
INSTRUMENT_ID | VALUE_TYPE_CODE | ORIGINAL_TRANSACTION_VALUE | VALUE_IN_HOME_CURRENCY
ABC           |TOT_AMT          |   3                        |  5.7702
ABC           |AMT_AVAL         |   1                        |  1.9234
ABC           |DRWN_AMT         |   2                        |  3.8468
ABC           |MTD_DRWN         |   2                        |  3.8468

ORIGINAL_TRANSACTION_VALUE

为中心
SELECT *
FROM
(
  SELECT INSTRUMENT_ID,
       VALUE_TYPE_CODE,
       ORIGINAL_TRANSACTION_VALUE,
       VALUE_IN_HOME_CURRENCY
  FROM FACT_TABLE
)
AS Table
PIVOT (MAX (ORIGINAL_TRANSACTION_VALUE) FOR  VALUE_TYPE_CODE IN ([TOT_AMT], 
                                                                [AMT_AVAL], 
                                                                [DRWN_AMT], 
                                                                [MTD_DRWN])) as piv

结果如预期:

INSTRUMENT_ID | TOT_AMT | AMT_AVAL | DRWN_AMT | MTD_DRWN
ABC           |3        | 1        | 2        | 2

但是,我正在尝试生成以下内容 - 请注意每个 VALUE_TYPE_CODE 类别的交替 ORIGINAL_TRANSACTION_VALUE 和 VALUE_IN_HOME_CURRENCY 值

INSTRUMENT_ID |TOT_AMT  |TOT_AMT |AMT_AVAL |AMT_AVAL |DRWN_AMT |DRWN_AMT |MTD_DRWN |MTD_DRWN
ABC           | 3       |5.7702  |  1      |1.9234   |2        |3.8468   |  2      |3.8468

所以我尝试了从这里找到的以下内容。它看起来很简单,而且我还是新 SQL,所以对我来说很有意义。复制类别值,否则 PIVOT 将无法正常工作。

SELECT *
FROM
(
  SELECT INSTRUMENT_ID,
       VALUE_TYPE_CODE,
       VALUE_TYPE_CODE+'1' as VALUE_TYPE_CODE1,
       ORIGINAL_TRANSACTION_VALUE,
       VALUE_IN_HOME_CURRENCY
  FROM FACT_TABLE
)
AS Table
-- Pivot on ORIGINAL_TRANSACTION_VALUE
PIVOT (MAX (ORIGINAL_TRANSACTION_VALUE) FOR  VALUE_TYPE_CODE IN ([TOT_AMT], 
                                                                 [AMT_AVAL], 
                                                                 [DRWN_AMT], 
                                                                 [MTD_DRWN])) as piv
-- Pivot on VALUE_IN_HOME_CURRENCY
PIVOT (MAX (VALUE_IN_HOME_CURRENCY) FOR  VALUE_TYPE_CODE1 IN ([TOT_AMT1], 
                                                              [AMT_AVAL1], 
                                                              [DRWN_AMT1], 
                                                              [MTD_DRWN1])) as piv1

不幸的是,这并没有按照我希望的方式工作,结果如下:

INSTRUMENT_ID |TOT_AMT  |TOT_AMT |AMT_AVAL |AMT_AVAL |DRWN_AMT |DRWN_AMT |MTD_DRWN |MTD_DRWN
ABC           | 3       |5.7702  |  NULL   |NULL     |NULL     |NULL     |NULL     |NULL   
ABC           | NULL    |NULL    |  1      | 1.9234  |NULL     |NULL     |NULL     |NULL 
ABC           | NULL    |NULL    |  NULL   |NULL     |2        |3.8468   |NULL     |NULL  
ABC           | NULL    |NULL    |  NULL   |NULL     |NULL     |NULL     |  2      |3.8468

我想知道是否有人能指出我做错了什么?有没有稍微简单一点的方法?本身可能不是一种更简单的方法,而是一种通过一些解释分解的解决方案。我在这里查看了一些解决方案,其中很多只是让我头疼,因为我还是新手。

我非常感谢任何帮助。谢谢。

【问题讨论】:

    标签: sql sql-server pivot


    【解决方案1】:

    主要问题是您为数据透视语句提供的列比您要透视的列多。 piv 不需要 VALUE_IN_HOME_CURRENCY 用于其枢轴操作,piv1 不需要 ORIGINAL_TRANSACTION_VALUE

    这个问题有几种解决方案:

    1. group by 和一些聚合添加到您当前的解决方案中。
    2. 将枢轴操作拆分为两个单独的操作,然后在 INSTRUMENT_ID 上重新加入。
    3. 取消透视原始数据,使ORIGINAL_TRANSACTION_VALUEVALUE_IN_HOME_CURRENCY 成为一列,然后一次性旋转为8 列。在单个列中获取所有值确实需要所有值的兼容数据类型。

    样本数据

    create table FACT_TABLE
    (
       INSTRUMENT_ID nvarchar(3),
       VALUE_TYPE_CODE nvarchar(10),
       ORIGINAL_TRANSACTION_VALUE int,
       VALUE_IN_HOME_CURRENCY money,
    );
    
    insert into FACT_TABLE (INSTRUMENT_ID, VALUE_TYPE_CODE, ORIGINAL_TRANSACTION_VALUE, VALUE_IN_HOME_CURRENCY) values
    ('ABC', 'TOT_AMT' , 3, 5.7702),
    ('ABC', 'AMT_AVAL', 1, 1.9234),
    ('ABC', 'DRWN_AMT', 2, 3.8468),
    ('ABC', 'MTD_DRWN', 2, 3.8468);
    

    解决方案 1

    SELECT piv1.INSTRUMENT_ID,
           max(piv1.TOT_AMT)   as TOT_AMT,
           max(piv1.TOT_AMT1)  as TOT_AMT1,
           max(piv1.AMT_AVAL)  as AMT_AVAL,
           max(piv1.AMT_AVAL1) as AMT_AVAL1,
           max(piv1.DRWN_AMT)  as DRWN_AMT,
           max(piv1.DRWN_AMT1) as DRWN_AMT1,
           max(piv1.MTD_DRWN)  as MTD_DRWN,
           max(piv1.MTD_DRWN1) as MTD_DRWN1
    FROM
    (
      SELECT INSTRUMENT_ID,
           VALUE_TYPE_CODE,
           VALUE_TYPE_CODE+'1' as VALUE_TYPE_CODE1,
           ORIGINAL_TRANSACTION_VALUE,
           VALUE_IN_HOME_CURRENCY
      FROM FACT_TABLE
    )
    AS T
    -- Pivot on ORIGINAL_TRANSACTION_VALUE
    PIVOT (MAX (ORIGINAL_TRANSACTION_VALUE) FOR  VALUE_TYPE_CODE IN ([TOT_AMT], 
                                                                     [AMT_AVAL], 
                                                                     [DRWN_AMT], 
                                                                     [MTD_DRWN])) as piv
    -- Pivot on VALUE_IN_HOME_CURRENCY
    PIVOT (MAX (VALUE_IN_HOME_CURRENCY) FOR  VALUE_TYPE_CODE1 IN ([TOT_AMT1], 
                                                                  [AMT_AVAL1], 
                                                                  [DRWN_AMT1], 
                                                                  [MTD_DRWN1])) as piv1
    group by piv1.INSTRUMENT_ID
    

    解决方案 2

    with cte_piv as
    (
      -- Pivot on ORIGINAL_TRANSACTION_VALUE
      select piv.INSTRUMENT_ID,
             piv.TOT_AMT,
             piv.AMT_AVAL,
             piv.DRWN_AMT,
             piv.MTD_DRWN
      from ( SELECT INSTRUMENT_ID, VALUE_TYPE_CODE, ORIGINAL_TRANSACTION_VALUE
             FROM FACT_TABLE ) T
      PIVOT (MAX (ORIGINAL_TRANSACTION_VALUE) FOR  VALUE_TYPE_CODE IN ([TOT_AMT], 
                                                                     [AMT_AVAL], 
                                                                     [DRWN_AMT], 
                                                                     [MTD_DRWN])) as piv
    ),
    cte_piv1 as
    (
      -- Pivot on VALUE_IN_HOME_CURRENCY
      select piv1.INSTRUMENT_ID,
             piv1.TOT_AMT,
             piv1.AMT_AVAL,
             piv1.DRWN_AMT,
             piv1.MTD_DRWN
      from ( SELECT INSTRUMENT_ID, VALUE_TYPE_CODE, VALUE_IN_HOME_CURRENCY
             FROM FACT_TABLE ) T
      PIVOT (MAX (VALUE_IN_HOME_CURRENCY) FOR  VALUE_TYPE_CODE IN ([TOT_AMT], 
                                                                  [AMT_AVAL], 
                                                                  [DRWN_AMT], 
                                                                  [MTD_DRWN])) as piv1
    )
    select p.INSTRUMENT_ID,
           p.TOT_AMT,
           p1.TOT_AMT,
           p.AMT_AVAL,
           p1.AMT_AVAL,
           p.DRWN_AMT,
           p1.DRWN_AMT,
           p.MTD_DRWN,
           p1.MTD_DRWN
    from cte_piv p
    join cte_piv1 p1 on p1.INSTRUMENT_ID = p.INSTRUMENT_ID
    

    解决方案 3

    with cte as
    (
      SELECT INSTRUMENT_ID,
             VALUE_TYPE_CODE,
              -- all values require a compatible type for a pivot
             convert(money, ORIGINAL_TRANSACTION_VALUE) as ORIGINAL_TRANSACTION_VALUE,
             VALUE_IN_HOME_CURRENCY
     FROM FACT_TABLE
    ),
    cte2 as
    (
      select up.INSTRUMENT_ID,
             up.VALUE_TYPE_CODE + '_' + left(up.ValueType, 4) as NEW_CODE,
             up.Value
      from cte
      unpivot (Value for ValueType in (ORIGINAL_TRANSACTION_VALUE, VALUE_IN_HOME_CURRENCY)) up
    )
    select p.INSTRUMENT_ID,
           convert(int, p.TOT_AMT_ORIG)  as TOT_AMT,
           p.TOT_AMT_VALU                as TOT_AMT1,
           convert(int, p.AMT_AVAL_ORIG) as AMT_AVAL,
           p.AMT_AVAL_VALU               as AMT_AVAL1,
           convert(int, p.DRWN_AMT_ORIG) as DRWN_AMT,
           p.DRWN_AMT_ORIG               as DRWN_AMT1,
           convert(int, p.MTD_DRWN_ORIG) as MTD_DRWN,
           p.DRWN_AMT_VALU               as MTD_DRWN1,
           convert(int, p.MTD_DRWN_ORIG) as MTD_DRWN,
           p.MTD_DRWN_VALU               as MTD_DRWN1
    from cte2
    pivot (max(Value) for NEW_CODE in ([TOT_AMT_ORIG],  [TOT_AMT_VALU],
                                       [AMT_AVAL_ORIG], [AMT_AVAL_VALU],
                                       [DRWN_AMT_ORIG], [DRWN_AMT_VALU],
                                       [MTD_DRWN_ORIG], [MTD_DRWN_VALU])) p
    

    Fiddle 1 = 解决方案 1 和 2
    Fiddle 2 = 解决方案 3

    【讨论】:

    • 谢谢你!从您提供的解决方案列表中,考虑到我的整体查询很大,对我来说,考虑到我的情况,解决方案 2 通常看起来很直接。我想问的一件事..似乎 WITH + CTE 在整体查询之前?
    【解决方案2】:

    此答案适用于希望使其动态化的观众。 [列值是动态的]

    对于 MSSQL SERVER,

    DECLARE @cols1 AS NVARCHAR(MAX),@cols2 AS NVARCHAR(MAX),@selectcolumn1 AS NVARCHAR(MAX),@selectcolumn2 AS NVARCHAR(MAX),
       @selectcolumn AS NVARCHAR(MAX), @query  AS NVARCHAR(MAX);
        SET @cols1 = STUFF((SELECT distinct ',' + QUOTENAME(f.VALUE_TYPE_CODE) 
                FROM FACT_TABLE f
                FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(MAX)') 
            ,1,1,'') 
        SET @cols2 = STUFF((SELECT distinct ',' + QUOTENAME(f.VALUE_TYPE_CODE+'1')
                FROM FACT_TABLE f
                FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(MAX)') 
            ,1,1,'') 
    
        SET @selectcolumn1 = STUFF((SELECT distinct ',MAX(piv1.' + f.VALUE_TYPE_CODE+') as '+ f.VALUE_TYPE_CODE+'_OTV'
            FROM FACT_TABLE f
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'') 
    
        SET @selectcolumn2 = STUFF((SELECT distinct ',MAX(piv1.' + f.VALUE_TYPE_CODE+'1) as '+ f.VALUE_TYPE_CODE+'_HC'
            FROM FACT_TABLE f
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')
    
      set @selectcolumn=concat(@selectcolumn1,',',@selectcolumn2)
    
    set @query = 'SELECT piv1.INSTRUMENT_ID,
           '+@selectcolumn+'
    FROM
    (
      SELECT INSTRUMENT_ID,
           VALUE_TYPE_CODE,
           VALUE_TYPE_CODE+''1'' as VALUE_TYPE_CODE1,
           ORIGINAL_TRANSACTION_VALUE,
           VALUE_IN_HOME_CURRENCY
      FROM FACT_TABLE
    )
    AS T
    PIVOT (MAX (ORIGINAL_TRANSACTION_VALUE) FOR  VALUE_TYPE_CODE IN ('+@cols1+')) as piv
    PIVOT (MAX (VALUE_IN_HOME_CURRENCY) FOR  VALUE_TYPE_CODE1 IN ('+@cols2+')) as piv1
                                                                  
    group by piv1.INSTRUMENT_ID'
    
    execute(@query)
    
    

    对于 MYSQL 服务器

    SET @col1 = NULL;
    SET @col2 = NULL;
    SET @sql = NULL;
    SELECT
      GROUP_CONCAT(DISTINCT
        CONCAT(      
     
           'Max(IF(f.VALUE_TYPE_CODE = ''',VALUE_TYPE_CODE,''', ORIGINAL_TRANSACTION_VALUE, Null)) as ',VALUE_TYPE_CODE,'_OT'
        )
      ) INTO @col1
    FROM FACT_TABLE;
    
    SELECT
      GROUP_CONCAT(DISTINCT
        CONCAT(      
     
           'Max(IF(f.VALUE_TYPE_CODE1 = ''',VALUE_TYPE_CODE,'1'', VALUE_IN_HOME_CURRENCY, Null)) as ',VALUE_TYPE_CODE,'_HC'
        )
      ) INTO @col2
    FROM FACT_TABLE;
    
    
    
    SET @sql = CONCAT('SELECT 
        f.INSTRUMENT_ID,  ', CONCAT(@col1,',',@col2) , 
        ' FROM  (SELECT INSTRUMENT_ID,VALUE_TYPE_CODE,CONCAT(VALUE_TYPE_CODE,''1'') AS VALUE_TYPE_CODE1,ORIGINAL_TRANSACTION_VALUE,VALUE_IN_HOME_CURRENCY FROM FACT_TABLE) f
       Group by f.INSTRUMENT_ID');
      
    PREPARE stmt FROM @sql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
       
    
    
    
    

    【讨论】:

      【解决方案3】:

      PIVOT 要记住的是,它会在该点转换整个结果集。 PIVOT 中未提及的所有列都有效地用于将结果分组到行中。

      因此,在您的尝试中,您的第一个 PIVOT 根据 INSTRUMENT_IDVALUE_IN_HOME_CURRENCY 对行进行分组,这就是它产生四行的原因。

      解决方案是将原始结果集旋转两次,排除不必要的列,然后加入结果。

      所以让我们使用公用表表达式来分解它:

      WITH SourceData as (
          SELECT INSTRUMENT_ID,
             VALUE_TYPE_CODE,
             ORIGINAL_TRANSACTION_VALUE,
             VALUE_IN_HOME_CURRENCY
          FROM FACT_TABLE
      ), OriginalPivotSource as (
          SELECT
             INSTRUMENT_ID,
             VALUE_TYPE_CODE,
             ORIGINAL_TRANSACTION_VALUE
          FROM SourceData
      ), OriginalPivot as (
          SELECT
              *
          FROM OriginalPivotSource
          PIVOT (MAX (ORIGINAL_TRANSACTION_VALUE) FOR  VALUE_TYPE_CODE IN ([TOT_AMT], 
                                                                       [AMT_AVAL], 
                                                                       [DRWN_AMT], 
                                                                       [MTD_DRWN])) t
      ), HomePivotSource as (
          SELECT
             INSTRUMENT_ID,
             VALUE_TYPE_CODE,
             VALUE_IN_HOME_CURRENCY
          FROM
              SourceData
      ), HomePivot as (
          SELECT
              *
          FROM HomePivotSource
          PIVOT (MAX (VALUE_IN_HOME_CURRENCY) FOR  VALUE_TYPE_CODE IN ([TOT_AMT], 
                                                                    [AMT_AVAL], 
                                                                    [DRWN_AMT], 
                                                                    [MTD_DRWN])) t
      )
      SELECT
          *
      FROM
          OriginalPivot op
              FULL OUTER JOIN
          HomePivot hp
              on
                 op.INSTRUMENT_ID = hp.INSTRUMENT_ID
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-18
        • 2015-10-01
        相关资源
        最近更新 更多