所以我正在查询一些非常大的表。它们如此之大的原因是因为PeopleSoft每次对某些数据进行更改时都会插入新记录,而不是更新现有记录。实际上,它的事务表也是一个数据仓库。
这需要在其中嵌套选择的查询,以获取最新/当前行。它们都是有效的,并且在每个日期内(施放到一天)它们可以具有有效的序列。因此,为了获得user_id=123
的当前记录,我必须这样做:
select * from sometable st
where st.user_id = 123
and st.effective_date = (select max(sti.effective_date)
from sometable sti where sti.user_id = st.user_id)
and st.effective_sequence = (select max(sti.effective_sequence)
from sometable sti where sti.user_id = st.user_id
and sti.effective_date = st.effective_date)
这些表上有非常多的索引,我找不到任何可以加快查询速度的其他索引。
我的麻烦是,我经常想要从这些表中获取有关个人的数据,可能有50个user_id,但是当我加入我的表中只有几条记录并带有一些这些PeopleSoft表时,事情就这样了垃圾。
PeopleSoft表位于我通过数据库链接访问的远程数据库中。我的查询看起来像这样:
select st.* from local_table lt, sometable@remotedb st
where lt.user_id in ('123', '456', '789')
and lt.user_id = st.user_id
and st.effective_date = (select max(sti.effective_date)
from sometable@remotedb sti where sti.user_id = st.user_id)
and st.effective_sequence = (select max(sti.effective_sequence)
from sometable@remotedb sti where sti.user_id = st.user_id
and sti.effective_date = st.effective_date)
当我必须使用本地表连接多个PeopleSoft表时,事情变得更糟。表现是不可接受的。
我可以做些什么来提高性能?我已经尝试过查询提示,以确保我的本地表首先加入PeopleSoft中的伙伴,因此在将其缩小到正确的user_id之前,它不会尝试将所有表连接在一起。我已经尝试了LEADING
提示并玩弄了一些试图将处理推送到远程数据库的提示,但解释计划被模糊了,只是对几个操作说'REMOTE'而且我不知道是什么正在进行中。
假设我没有权力更改PeopleSoft和我的桌子的位置,这些提示是我的最佳选择吗?如果我加入一个带有四个远程表的本地表,并且本地表与两个远程表连接,我将如何格式化提示以便我的本地表(这是非常小的 - 事实上,我可以只进行内联视图让我的本地表只是我感兴趣的user_ids)首先与每个远程表连接?
编辑:应用程序需要实时数据,因此不幸的是,物化视图或其他缓存数据的方法是不够的。
答案 0 :(得分:4)
重构你的查询是否有点像这样的帮助?
SELECT *
FROM (SELECT st.*, MAX(st.effective_date) OVER (PARTITION BY st.user_id) max_dt,
MAX(st.effective_sequence) OVER (PARTITION BY st.user_id, st.effective_date) max_seq
FROM local_table lt JOIN sometable@remotedb st ON (lt.user_id = st.user_id)
WHERE lt.user_id in ('123', '456', '789'))
WHERE effective_date = max_dt
AND effective_seq = max_seq;
我同意@Mark Baker的说法,加入DB Links的表现真的很糟糕,而且你可能会受限于你用这种方法可以完成的任务。
答案 1 :(得分:4)
一种方法是将PL / SQL函数粘贴到所有内容中。 作为一个例子
create table remote (user_id number, eff_date date, eff_seq number, value varchar2(10));
create type typ_remote as object (user_id number, eff_date date, eff_seq number, value varchar2(10));
.
/
create type typ_tab_remote as table of typ_remote;
.
/
insert into remote values (1, date '2010-01-02', 1, 'a');
insert into remote values (1, date '2010-01-02', 2, 'b');
insert into remote values (1, date '2010-01-02', 3, 'c');
insert into remote values (1, date '2010-01-03', 1, 'd');
insert into remote values (1, date '2010-01-03', 2, 'e');
insert into remote values (1, date '2010-01-03', 3, 'f');
insert into remote values (2, date '2010-01-02', 1, 'a');
insert into remote values (2, date '2010-01-02', 2, 'b');
insert into remote values (2, date '2010-01-03', 1, 'd');
create function show_remote (i_user_id_1 in number, i_user_id_2 in number) return typ_tab_remote pipelined is
CURSOR c_1 is
SELECT user_id, eff_date, eff_seq, value
FROM
(select user_id, eff_date, eff_seq, value,
rank() over (partition by user_id order by eff_date desc, eff_seq desc) rnk
from remote
where user_id in (i_user_id_1,i_user_id_2))
WHERE rnk = 1;
begin
for c_rec in c_1 loop
pipe row (typ_remote(c_rec.user_id, c_rec.eff_date, c_rec.eff_seq, c_rec.value));
end loop;
return;
end;
/
select * from table(show_remote(1,null));
select * from table(show_remote(1,2));
您可以将它们加载到本地表(例如全局临时表)中,而不是将user_id作为参数单独传递。然后PL / SQL将循环遍历表,为本地表中的每一行进行远程选择。没有一个查询可以同时拥有本地和远程表。实际上,您将编写自己的加入代码。
答案 2 :(得分:3)
一个选项是首先使用公共表表达式实现查询的远程部分,这样您就可以确保只从远程数据库中获取相关数据。另一个改进是将2个子查询与远程数据库合并为一个分析基于函数的子查询。这样的查询也可以在您当前的查询中使用。只有在玩了db之后我才能提出其他建议。
见下文
with remote_query as
(
select /*+ materialize */ st.* from sometable@remotedb st
where st.user_id in ('123', '456', '789')
and st.rowid in( select first_value(rowid) over (order by effective_date desc,
effective_sequence desc ) from sometable@remotedb st1
where st.user_id=st1.user_id)
)
select lt.*,st.*
FROM local_table st,remote_query rt
where st.user_id=rt.user_id
答案 3 :(得分:1)
您尚未提及数据新鲜度的要求,但有一个选项是创建实体化视图(您将限制为REFRESH COMPLETE,因为您无法在源系统中创建快照日志)数据仅用于事务表的当前版本化行。这些物化视图表将驻留在本地系统中,并且可以向其添加其他索引以提高查询性能。
答案 4 :(得分:1)
性能问题将是跨链接的访问。对于本地表的部分查询,它全部在本地执行,因此无法访问远程索引,并且它将所有远程数据拉回到lkocally测试。
如果您可以定期(每晚)使用在peoplesoft数据库中刷新的本地数据库中的物化视图来获取历史数据,那么只能访问远程peoplesoft数据库以获取今天的更改(在today子句中添加effective_date = today)并合并这两个查询。
另一种选择可能是使用INSERT INTO X SELECT FROM将远程数据拉入临时本地表或物化视图,然后使用第二个查询将其与本地数据连接...类似于josephj1989的建议
或者(尽管可能存在许可问题)尝试使用远程peoplesoft db对本地数据库进行RAC集群。
答案 5 :(得分:0)
您可以尝试使用子查询,而不是使用子查询。我不知道Oracle是否会在这方面表现更好,因为我不太使用Oracle。
SELECT
ST1.col1,
ST1.col2,
...
FROM
Some_Table ST1
LEFT OUTER JOIN Some_Table ST2 ON
ST2.user_id = ST1.user_id AND
(
ST2.effective_date > ST1.effective_date OR
(
ST2.effective_date = ST1.effective_date AND
ST2.effective_sequence > ST1.effective_sequence
)
)
WHERE
ST2.user_id IS NULL
另一种可能的解决方案是:
SELECT
ST1.col1,
ST1.col2,
...
FROM
Some_Table ST1
WHERE
NOT EXISTS
(
SELECT
FROM
Some_Table ST2
WHERE
ST2.user_id = ST1.user_id AND
(
ST2.effective_date > ST1.effective_date OR
(
ST2.effective_date = ST1.effective_date AND
ST2.effective_sequence > ST1.effective_sequence
)
)
)
答案 6 :(得分:0)
是否可以选择创建一个数据库,用于非仓库类型的东西,您可以每晚更新?如果是,您可以创建一个仅移动最新记录的夜间流程。这将消除你为每天查询所做的MAX事情,并显着减少数量或记录。
此外,取决于您是否可以在最新数据与可用数据之间有一天的时间间隔。
我对Oracle并不是很熟悉,所以通过更改查询也可能有办法改进...
答案 7 :(得分:0)
您可以将具有所需user_id的行ETL ETL到您自己的表中,只创建所需的索引来支持您的查询并对其执行查询吗?
答案 8 :(得分:0)
PeopleSoft表是交付的,还是自定义的?你确定它是一个物理表,而不是PS方面写得不好的视图吗?如果它是你要反对的交付记录(例子看起来很像PS_JOB或引用它的视图),也许你可以指出这一点。 PS_JOB是一个拥有大量索引的野兽,大多数网站都会增加更多。
如果您知道表中的索引,则可以使用Oracle提示指定要使用的首选索引;这有时会有所帮助。
您是否制定了解释计划,看看是否可以确定问题所在?也许有笛卡尔联合,全表扫描等?
答案 9 :(得分:0)
在我看来,您正在处理数据仓库中的类型2维度。有几种方法可以实现类型2维度,主要有ValidFrom, ValidTo, Version, Status
之类的列。并非所有这些都始终存在,如果您可以为您的表发布架构,那将会很有趣。以下是它的外观示例(约翰史密斯于2010-06-24从印第安纳州搬到俄亥俄州)
UserKey UserBusinessKey State ValidFrom ValidTo Version Status
7234 John_Smith_17 Indiana 2005-03-20 2010-06-23 1 expired
9116 John_Smith_17 Ohio 2010-06-24 3000-01-01 2 current
要获取行的最新版本,通常使用
WHERE Status = 'current'
或
WHERE ValidTo = '3000-01-01'
请注意,这个将来会有一些不变。
或
WHERE ValidTo > CURRENT_DATE
似乎您的示例使用ValidFrom
(effective_date),因此您必须找到max()
才能找到最新的行。看一下架构 - 表中是否有Status or ValidTo
个等价物?