Oracle SQL 查询从一行中的多个列中获取最新数据
Posted
技术标签:
【中文标题】Oracle SQL 查询从一行中的多个列中获取最新数据【英文标题】:Oracle SQL Query to get Latest Data from multiple columns in one row 【发布时间】: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,我只想获取在表上完成的任何更新,所以如果最初在创建员工记录时输入了所有字段。然后只更改拳头名称,然后我只想要名字而不是其他列值。我只想获取已更改的最新数据。 【参考方案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 表,其中每个员工一行,并在每次插入现有日志表时填充它的触发器。如果你经常需要这个,那是我会选择的路线。
【讨论】:
以上是关于Oracle SQL 查询从一行中的多个列中获取最新数据的主要内容,如果未能解决你的问题,请参考以下文章
SQL查询从特定列中的每个字段中删除某些文本 - Oracle SQL