【问题标题】:Oracle SQL Query to get Latest Data from multiple columns in one rowOracle SQL 查询从一行中的多个列中获取最新数据
【发布时间】:2020-04-16 07:35:57
【问题描述】:

我有这样的表 MY_TABLE,它存储来自许多表的更改数据(更新跟踪)。因此,每当其他表有一些更新时,我都会在基表上使用AFTER UPDATE TRIGGER 将新数据存储在 MY_TABLE 中。

ID  RECORD_DESC EMP_ID  FIRST_NAME  LAST_NAME   GENDER  SALARY
1   EMP         5       ABC         XYZ                 
2   EMP         5                               M       
3   EMP         5                   XYZ-NEW     F
4   SAL         5                                       1000
5   EMP         5                               M       
6   SAL         5       ABC-NEW                         750

现在我想查询 MY_TABLE 以获取所有列的最新更改的员工数据,结果应该是这样的行:

EMP_ID  FIRST_NAME  LAST_NAME   GENDER  SALARY
5       ABC-NEW     XYZ-NEW     M       750

到目前为止,我所做的是为每一列获取 MAX(ID),并从该 ID 再次查询表以获取该 ID 的列值。

但问题是这个查询会在 db 上承受相当大的负载,因为我有 25 个这样的列,并且表会随着时间的推移而变大。

那么,有人可以建议我更好的方法来编写下面的查询:

SELECT (SELECT FIRST_NAME FROM MY_TABLE WHERE ID = T2.FIRST_NAME_PK) AS FIRST_NAME
     , (SELECT LAST_NAME  FROM MY_TABLE WHERE ID = T2.LAST_NAME_PK ) AS LAST_NAME
     , (SELECT GENDER     FROM MY_TABLE WHERE ID = T2.GENDER_PK    ) AS GENDER
     , (SELECT SALARY     FROM MY_TABLE WHERE ID = T2.SALARY_PK    ) AS SALARY
  FROM (SELECT (SELECT MAX(ID) FROM MY_TABLE WHERE EMP_ID = T1.EMP_ID AND FIRST_NAME IS NOT NULL) FIRST_NAME_PK     -- ID = 6
             , (SELECT MAX(ID) FROM MY_TABLE WHERE EMP_ID = T1.EMP_ID AND LAST_NAME  IS NOT NULL) LAST_NAME_PK      -- ID = 3
             , (SELECT MAX(ID) FROM MY_TABLE WHERE EMP_ID = T1.EMP_ID AND GENDER     IS NOT NULL) GENDER_PK         -- ID = 5
             , (SELECT MAX(ID) FROM MY_TABLE WHERE EMP_ID = T1.EMP_ID AND SALARY     IS NOT NULL) SALARY_PK         -- ID = 6
          FROM (SELECT DISTINCT EMP_ID
                  FROM MY_TABLE
               ) T1
       ) T2;

【问题讨论】:

  • 发布示例数据的创建、插入语句并显示您想要的输出。
  • 请解释您为什么要这样做。您没有包含当前值的员工表吗?然后,您只需读取此表,这当然比从日志表中收集数据要快得多。
  • @ThorstenKettner,我只想获取在表上完成的任何更新,所以如果最初在创建员工记录时输入了所有字段。然后只更改拳头名称,然后我只想要名字而不是其他列值。我只想获取已更改的最新数据。

标签: sql oracle performance max


【解决方案1】:

试试这个

SELECT DISTINCT EMP_ID, 
, LAST_VALUE(FIRST_NAME) IGNORE NULLS OVER (PARTITION BY EMP_ID ORDER BY ID ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) 
, LAST_VALUE(LAST_NAME) IGNORE NULLS OVER (PARTITION BY EMP_ID ORDER BY ID ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) 
, LAST_VALUE(GENDER) IGNORE NULLS OVER (PARTITION BY EMP_ID ORDER BY ID ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) 
, LAST_VALUE(SALARY) IGNORE NULLS OVER (PARTITION BY EMP_ID ORDER BY ID ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) 
FROM MY_TABLE

【讨论】:

    【解决方案2】:

    您可以按如下方式使用KEEP 子句:

    SELECT ID,
           MAX(FIRST_NAME) KEEP (DENSE_RANK FIRST ORDER BY CASE WHEN FIRST_NAME IS NULL THEN NULL ELSE ID END DESC NULLS LAST) AS FIRST_NAME,
           MAX(LAST_NAME) KEEP (DENSE_RANK FIRST ORDER BY CASE WHEN LAST_NAME IS NULL THEN NULL ELSE ID END DESC NULLS LAST) AS LAST_NAME,
           MAX(GENDER) KEEP (DENSE_RANK FIRST ORDER BY CASE WHEN GENDER IS NULL THEN NULL ELSE ID END DESC NULLS LAST) AS GENDER,
           MAX(SALARY) KEEP (DENSE_RANK FIRST ORDER BY CASE WHEN SALARY IS NULL THEN NULL ELSE ID END DESC NULLS LAST) AS SALARY
      FROM MY_TABLE
    GROUP BY ID;
    

    【讨论】:

      【解决方案3】:

      这个怎么样?查看代码中的 cmets。

      SQL> with my_table (id, record_Desc, emp_id, first_name, last_name, gender, salary) as
        2    -- sample data
        3    (select 1, 'emp', 5, 'abc', 'xyz', null , null from dual union all
        4     select 2, 'emp', 5, null, null, 'm', null from dual union all
        5     select 3, 'emp', 5, null, 'xyz-new', 'f', null from dual union all
        6     select 4, 'emp', 5, null, null, null, 1000 from dual union all
        7     select 5, 'emp', 5, null, null, 'm', null from dual union all
        8     select 6, 'emp', 5, 'abc-new', null, null, 750 from dual
        9    ),
       10  temp as
       11    -- find last values
       12    (select a.id,
       13      a.record_desc,
       14      a.emp_id,
       15      last_value(a.first_name ignore nulls) over (partition by a.record_desc, a.emp_id order by a.id) first_name,
       16      last_value(a.last_name  ignore nulls) over (partition by a.record_desc, a.emp_id order by a.id) last_name,
       17      last_value(a.gender     ignore nulls) over (partition by a.record_desc, a.emp_id order by a.id) gender,
       18      last_value(a.salary     ignore nulls) over (partition by a.record_desc, a.emp_id order by a.id) salary
       19     from my_table a
       20    )
       21  -- extract only the last row per RECORD_DESC and EMP_ID
       22  select *
       23  from temp c
       24  where c.id = (select max(b.id) From my_table b
       25                where b.record_desc = c.record_Desc
       26                  and b.emp_id = c.emp_id
       27               );
      
              ID REC     EMP_ID FIRST_N LAST_NA G     SALARY
      ---------- --- ---------- ------- ------- - ----------
               6 emp          5 abc-new xyz-new m        750
      
      SQL>
      

      【讨论】:

        【解决方案4】:

        您的员工表包含当前数据。不过,您只想显示已更改的数据。

        我要做的是显示员工数据,以防列在日志表中有更新。我们不必查找最新的更新,因为无论该列多久更新一次,employee 表都包含最后一个值。尽管必须读取整个日志表,但这是一个非常简单的操作。

        select
          e.emp_id,
          case when log.some_first_name is not null then e.first_name end as first_name,
          case when log.some_last_name  is not null then e.last_name  end as last_name,
          case when log.some_gender     is not null then e.gender     end as gender,
          case when log.some_salary     is not null then e.salary     end as salary
        from employees e
        join
        (
          select
            emp_id,
            min(first_name) as some_first_name,
            min(last_name)  as some_last_name,
            min(gender)     as some_gender,
            min(salary)     as some_salary
          from my_table
          group by emp_id
        ) log on log.emp_id = e.emp_id
        order by e.emp_id;
        

        一次又一次地运行此查询的替代方法是使用 last_updates 表,其中每个员工一行,并在每次插入现有日志表时填充它的触发器。如果你经常需要这个,那是我会选择的路线。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-11-29
          • 2017-04-05
          • 1970-01-01
          • 2020-09-26
          • 2022-01-06
          相关资源
          最近更新 更多