我是Oracle的新手。 我写了一个约需40秒的查询
SELECT count(*) FROM (
SELECT * FROM (SELECT r.idra FROM hm.Ra r
WHERE (r.rxdate BETWEEN to_date('2013-09-01','YYYY-MM-DD') and to_date('2013-11-01','YYYY-MM-DD'))
and (r.idstato IN (1,2,3,4,5,6))
and ((r.idgruppo IN (864,863...,595))
or (r.curridgroup IN (864,863...,595))
or (EXISTS (SELECT rev.idra FROM hm.Raevent rev WHERE rev.idra = r.idra AND rev.idgroup IS NOT NULL AND rev.idgroup IN (864,863...,595))) ) ) ) r1
稍微搜索一下后,我用内连接重写了查询,大概需要1秒:
select count(*)
FROM
(SELECT r.idra FROM hm.v_Ra r
WHERE (r.rxdate BETWEEN to_date('2013-09-01','YYYY-MM-DD') and to_date('2013-11-01','YYYY-MM-DD'))
and (r.idstato IN (1,2,3,4,5,6))) rd
inner join
((SELECT idra FROM hm.Ra r
WHERE ((r.idgruppo IN (864,863...,595))
or (r.curridgroup IN (864,863...,595)))
UNION
SELECT rev.idra FROM hm.Raevent rev WHERE rev.idgroup IS NOT NULL
AND rev.idgroup IN (864,863...,595)
)) rr on rr.idra = rd.idra
我的问题是:为什么oracle(版本10gr2)无法优化第一个? 另外,是否有另一种(更快)的方法来编写相同的查询?
注意:列表(864,863 ...,595)有大约30个元素,为方便起见我写了3。 “Ra”是父表,并且有大约150k行,rxdate,idgruppo,curridgroup,idstato上的索引。 “Raevent”是一个子表,有180k行,索引(idra,idgroup),idgroup。
我运行sql分析器,在两个查询中它都没有给我任何建议。
答案 0 :(得分:1)
清楚的问题和良好的信息,太棒了!
您正在运行OR-clauses的优化问题。这是一个已知的限制,可以追溯到Oracle 6,导致基于规则的优化器出现问题。我已经在查询上做了很多笔记来帮助你。
首先,始终以可读格式编写查询,以识别人类的结构。解析器并不关心它是如何布局的,但人类会这样做: - )
我已经习惯了旧的Oracle QMS / CDM风格,并增加了一些功能。这种风格还反映了IBM完成的软件工程研究和信息人体工程学的经验。本质上,查询以垂直拉伸格式写下,垂直列和小写字母的使用,因为小写总是人类的头脑更容易识别文本(与几乎闭合的眼睛相比:'PERSONEELSDOSSIER'与'Personeelsdossier')。重写布局后,查询为:
select count (*)
from ( select *
from ( select r.idra
from hm.ra r
where r.rxdate between to_date ('2013-09-01', 'yyyy-mm-dd') and to_date ('2013-11-01', 'yyyy-mm-dd')
and r.idstato in (1, 2, 3, 4, 5, 6)
and ( r.idgruppo in (864, 863...,595)
or r.curridgroup in (864, 863..., 595)
or ( exists
( select rev.idra
from hm.raevent rev
where rev.idra = r.idra
and rev.idgroup is not null
and rev.idgroup in (864, 863..., 595)
)
)
)
)
) r
然后,您可以提出一些改善绩效的建议:
select count (*)
from ( select 1 /* Note 1. */
from ( select r.idra /* Note 2. */
from hm.ra r
where r.rxdate between to_date ('2013-09-01', 'yyyy-mm-dd') and to_date ('2013-11-01', 'yyyy-mm-dd') /* Note 3. */
and r.idstato in (1, 2, 3, 4, 5, 6)
and ( r.idgruppo in (864, 863...,595)
or r.curridgroup in (864, 863..., 595) /* Note 6. */
or ( exists /* Note 5. */
( select 1 /* Note 4. */
from hm.raevent rev
where rev.idra = r.idra
and rev.idgroup is not null
and rev.idgroup in (864, 863..., 595)
)
)
)
)
) r
建议如下:
生成的查询可能是:
select sum(c)
from ( select count(*) c
from hm.ra r
where r.rxdate between to_date ('2013-09-01', 'yyyy-mm-dd') and to_date ('2013-11-01', 'yyyy-mm-dd')
and r.idstato in (1, 2, 3, 4, 5, 6)
and r.idgruppo in (864, 863...,595)
union all
select count(*) c
from hm.ra r
where r.rxdate between to_date ('2013-09-01', 'yyyy-mm-dd') and to_date ('2013-11-01', 'yyyy-mm-dd')
and r.idstato in (1, 2, 3, 4, 5, 6)
and r.curridgroup in (864, 863..., 595)
union all
select count(*) c
from hm.ra r
where r.rxdate between to_date ('2013-09-01', 'yyyy-mm-dd') and to_date ('2013-11-01', 'yyyy-mm-dd')
and r.idstato in (1, 2, 3, 4, 5, 6)
and r.idra
in /* Use exists when used interactively in a screen. */
( select distinct rev.idra
from hm.raevent rev
where rev.idgroup in (864, 863..., 595)
)
)