Oracle SQL Запрос на получение последних данных из нескольких столбцов в одной строке - PullRequest
0 голосов
/ 16 апреля 2020

У меня есть такая таблица MY_TABLE, в которой хранятся измененные данные (отслеживание обновлений) из многих таблиц. Поэтому, когда в другой таблице есть какое-либо обновление, я сохраняю новые данные в MY_TABLE, используя AFTER UPDATE TRIGGER для базовых таблиц.

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.

Но проблема в том, что этот запрос будет испытывать значительную нагрузку на БД, потому что у меня 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;

Ответы [ 4 ]

2 голосов
/ 16 апреля 2020

Попробуйте это

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
1 голос
/ 16 апреля 2020

Ваша таблица сотрудников имеет текущие данные. Вы хотите только показать данные, которые были изменены, однако.

Что бы я сделал, это показал бы данные о сотрудниках в случае обновления столбца в таблице журнала. Нам не нужно искать последнее обновление, потому что независимо от того, как часто обновлялся столбец, таблица сотрудников содержит последнее значение. Это очень простая операция, несмотря на необходимость чтения всей таблицы журнала.

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 с одной строкой на сотрудника и триггером, который заполняет это при каждой вставке в существующую таблицу журналов. Если вам это нужно часто, я бы выбрал этот маршрут.

1 голос
/ 16 апреля 2020

Как насчет этого? См комментарии в коде.

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>
1 голос
/ 16 апреля 2020

Вы можете использовать предложение 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;
...