【问题标题】:flattening oracle table rows to a single row with multiple columns将 oracle 表行展平为具有多列的单行
【发布时间】:2017-11-08 23:59:13
【问题描述】:

我有一个审核表来维护用户状态作为它何时发生变化。 该表如下所示。

USER_ENTL_ID    USER_STATUS CREATED_Date
1                  S         10/20/2017
1                  C         10/21/2017
1                  W         10/22/2017
1                  SP        10/23/2017
2                  S         10/24/2017
2                  C         10/25/2017

每个用户可能不会转换到应用程序中的每个状态,这没关系。我想要做的是捕获每个用户更改时的日期和状态并将其放在一行中。

所以我期望它的输出如下所示。

id  S_status    s_created   c_status    c_created   W_status    w_created   sp_STATUS   SP_CREATED
1   S           10/20/2017    C         10/21/2017   W          10/22/2017      SP      10/23/2017
2   S           10/24/2017    C         10/25/2017              

我一直在阅读有关 pivot 和 unpivot 和 decode 的内容,但我不确定这在 oracle 中是否可行。如果是的话,有人可以引导我走上正确的道路吗?

【问题讨论】:

    标签: sql oracle oracle11g dynamic-queries


    【解决方案1】:

    使用“条件聚合”是处理此类需求的传统方式,并且仍然有效:

    SQL Fiddle

    PostgreSQL 9.6 架构设置

    CREATE TABLE AUDIT_TABLE
        (USER_ENTL_ID int, USER_STATUS varchar(2), CREATED_DATE timestamp)
    ;
    
    INSERT INTO AUDIT_TABLE
        (USER_ENTL_ID, USER_STATUS, CREATED_DATE)
    VALUES
        (1, 'S', '2017-10-20 00:00:00'),
        (1, 'C', '2017-10-21 00:00:00'),
        (1, 'W', '2017-10-22 00:00:00'),
        (1, 'SP', '2017-10-23 00:00:00'),
        (2, 'S', '2017-10-24 00:00:00'),
        (2, 'C', '2017-10-25 00:00:00')
    ;
    

    查询 1

    nb,在此使用 MIN 或 MAX 可能很重要,具体取决于您的数据,但我在每个输出位置的数据中只有一个值,然后可以使用任一函数。

    SELECT
          USER_ENTL_ID
        , MAX(CASE WHEN USER_STATUS = 'S' THEN USER_STATUS END) s_status
        , MIN(CASE WHEN USER_STATUS = 'S' THEN CREATED_DATE END) s_created
        , MAX(CASE WHEN USER_STATUS = 'C' THEN USER_STATUS END) c_status
        , MIN(CASE WHEN USER_STATUS = 'C' THEN CREATED_DATE END) c_created
        , MAX(CASE WHEN USER_STATUS = 'W' THEN USER_STATUS END) w_status
        , MIN(CASE WHEN USER_STATUS = 'W' THEN CREATED_DATE END) w_created
        , MAX(CASE WHEN USER_STATUS = 'SP' THEN USER_STATUS END) sp_status
        , MIN(CASE WHEN USER_STATUS = 'SP' THEN CREATED_DATE END) sp_created
    FROM AUDIT_TABLE
    GROUP BY 
          USER_ENTL_ID
    

    Results

    | user_entl_id | s_status |            s_created | c_status |            c_created | w_status |            w_created | sp_status |           sp_created |
    |--------------|----------|----------------------|----------|----------------------|----------|----------------------|-----------|----------------------|
    |            1 |        S | 2017-10-20T00:00:00Z |        C | 2017-10-21T00:00:00Z |        W | 2017-10-22T00:00:00Z |        SP | 2017-10-23T00:00:00Z |
    |            2 |        S | 2017-10-24T00:00:00Z |        C | 2017-10-25T00:00:00Z |   (null) |               (null) |    (null) |               (null) |
    

    添加

    进一步解释:如果您删除 MIN 或 MAX 函数并删除 group by,这就是您得到的:

    +--------------+----------+----------------------+----------+----------------------+----------+----------------------+-----------+----------------------+
    | user_entl_id | s_status |      s_created       | c_status |      c_created       | w_status |      w_created       | sp_status |      sp_created      |
    +--------------+----------+----------------------+----------+----------------------+----------+----------------------+-----------+----------------------+
    |            1 | S        | 2017-10-20T00:00:00Z | (null)   | (null)               | (null)   | (null)               | (null)    | (null)               |
    |            1 | (null)   | (null)               | C        | 2017-10-21T00:00:00Z | (null)   | (null)               | (null)    | (null)               |
    |            1 | (null)   | (null)               | (null)   | (null)               | W        | 2017-10-22T00:00:00Z | (null)    | (null)               |
    |            1 | (null)   | (null)               | (null)   | (null)               | (null)   | (null)               | SP        | 2017-10-23T00:00:00Z |
    |            2 | S        | 2017-10-24T00:00:00Z | (null)   | (null)               | (null)   | (null)               | (null)    | (null)               |
    |            2 | (null)   | (null)               | C        | 2017-10-25T00:00:00Z | (null)   | (null)               | (null)    | (null)               |
    +--------------+----------+----------------------+----------+----------------------+----------+----------------------+-----------+----------------------+
    

    如果您研究过,您会发现对于我们关心的数据,每行只有一个值(对于每个 USER_ENTL_ID),但它们分布在多行中。所以 MIN/MAX 函数和 GROUP BY “扁平化”了结果,所以我们最终得到了想要的结果。量子力学

    【讨论】:

    • max 和 min 函数与 user_status 和 created date 有什么关系?你能详细说明一下吗,我看到你在创建 sp_status 字段时添加了 max 函数,并在创建创建字段时添加了 min 函数?
    • 好的,我再次阅读了您的评论,看起来我使用哪个功能并不重要,对吗?但是这些函数实际上在这里做什么?
    • 在回复中添加了一些额外信息
    【解决方案2】:

    类似下面的内容可能更容易理解:

    WITH cteID AS (SELECT DISTINCT USER_ENTL_ID AS ID FROM AUDIT_TABLE),
         cteS  AS (SELECT USER_ENTL_ID AS ID,
                          'S' AS S_STATUS,
                          MIN(CREATED_DATE) AS S_CREATED
                     FROM AUDIT_TABLE
                     WHERE STATUS = 'S'
                     GROUP BY USER_ENTL_ID),
         cteC AS  (SELECT USER_ENTL_ID AS ID,
                          'C' AS C_STATUS,
                          MIN(CREATED_DATE) AS C_CREATED
                     FROM AUDIT_TABLE
                     WHERE STATUS = 'C'
                     GROUP BY USER_ENTL_ID),
         cteSP AS (SELECT USER_ENTL_ID AS ID,
                          'SP' AS SP_STATUS,
                          MIN(CREATED_DATE) AS SP_CREATED
                     FROM AUDIT_TABLE
                     WHERE STATUS = 'SP'
                     GROUP BY USER_ENTL_ID),
         cteW AS  (SELECT USER_ENTL_ID AS ID,
                          'W' AS W_STATUS,
                          MIN(CREATED_DATE) AS W_CREATED
                     FROM AUDIT_TABLE
                     WHERE STATUS = 'W'
                     GROUP BY USER_ENTL_ID)
    SELECT i.ID,
           s.S_STATUS,
           s.S_CREATED,
           c.C_STATUS,
           c.C_CREATED,
           sp.SP_STATUS,
           sp.SP_CREATED,
           w.W_STATUS,
           w.W_CREATED
      FROM cteID i
      LEFT OUTER JOIN cteS s
        ON s.ID = i.ID
      LEFT OUTER JOIN cteC c
        ON c.ID = i.ID
      LEFT OUTER JOIN cteSP sp
        ON sp.ID = i.ID
      LEFT OUTER JOIN cteW w
        ON w.ID = i.ID
    

    祝你好运。

    【讨论】:

      【解决方案3】:

      只需使用PIVOT:

      SQL Fiddle

      Oracle 11g R2 架构设置

      CREATE TABLE AUDIT_TABLE (USER_ENTL_ID, USER_STATUS, CREATED_DATE) AS
      SELECT 1, 'S',  TIMESTAMP '2017-10-20 00:00:00' FROM DUAL UNION ALL
      SELECT 1, 'C',  TIMESTAMP '2017-10-21 00:00:00' FROM DUAL UNION ALL
      SELECT 1, 'W',  TIMESTAMP '2017-10-22 00:00:00' FROM DUAL UNION ALL
      SELECT 1, 'SP', TIMESTAMP '2017-10-23 00:00:00' FROM DUAL UNION ALL
      SELECT 2, 'S',  TIMESTAMP '2017-10-24 00:00:00' FROM DUAL UNION ALL
      SELECT 2, 'C',  TIMESTAMP '2017-10-25 00:00:00' FROM DUAL
      

      查询 1

      SELECT *
      FROM   AUDIT_TABLE
      PIVOT (
        MAX( Created_Date ) AS Created,
        MAX( User_Status ) AS  Status
        FOR User_Status IN (
          'S' AS S, 'C' AS C, 'W' AS W, 'SP' AS SP
        )
      )
      

      Results

      | USER_ENTL_ID |             S_CREATED | S_STATUS |             C_CREATED | C_STATUS |             W_CREATED | W_STATUS |            SP_CREATED | SP_STATUS |
      |--------------|-----------------------|----------|-----------------------|----------|-----------------------|----------|-----------------------|-----------|
      |            1 | 2017-10-20 00:00:00.0 |        S | 2017-10-21 00:00:00.0 |        C | 2017-10-22 00:00:00.0 |        W | 2017-10-23 00:00:00.0 |        SP |
      |            2 | 2017-10-24 00:00:00.0 |        S | 2017-10-25 00:00:00.0 |        C |                (null) |   (null) |                (null) |    (null) |
      

      【讨论】:

        猜你喜欢
        • 2017-10-08
        • 2021-03-31
        • 1970-01-01
        • 1970-01-01
        • 2016-07-18
        • 1970-01-01
        • 2015-09-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多