我有一个像表一样的大型实体属性值。我尝试用子查询从该表中选择一些行,然后用行过滤。在这种情况下,如何防止合并子查询和主查询?
例如:
EMP:
EMPNO | ENAME | SAL
---------------------
1000 | KING | 10000
1001 | BLAKE | 7500
CREATE VIEW EAV(ID,ATTR,VALUE) AS
select empno, 'name'||ename, ename from emp -- subquery 1
union
select empno, 'sal'||ename, ename from emp -- subquery 2
union
select empno, 'mgr'||ename, ename from emp -- subquery 3
注意:添加||ename
只是为了防止Oracle通过添加过滤器来优化下一个查询"(null不为空)"子查询1和3
在子查询中,我选择所有具有属性' sal%'的行。然后在主查询中将其过滤掉:
select *
FROM (select id,value from EAV where attr like 'sal%')
WHERE to_number(value) > 5000;
此查询失败导致优化程序将子查询与外部查询合并。合并DB后,尝试将to_number应用于列" value"中的所有值,但其中一些值具有字符串值。 Witch HINT阻止了这种优化吗?
P.S。我希望得到与
相同的结果WITH t as (
select /*+ materialize */ id,value
from eav
where attr like 'sal%')
select * from t where to_number(value) > 5000;
但是,没有CTE。
答案 0 :(得分:1)
ROWNUM
是阻止优化程序转换并确保类型安全的最安全方法。使用ROWNUM
使Oracle认为行顺序很重要,并阻止了谓词推送和查看合并等内容。
select *
from
(
select id, value, rownum --Add ROWNUM for type safety.
from eav
where attr like 'sal%'
)
where to_number(value) > 5000;
还有其他方法可以做到这一点,但它们都不可靠。不要为简单的内联视图,公用表表达式,CASE
,谓词排序或提示而烦恼。那些常见的方法并不可靠,我看到它们都失败了。
最好的长期解决方案是改变EAV表,使每种类型都有不同的列,正如我在this answer中所描述的那样。现在解决这个问题,或者未来的开发人员在编写复杂查询以避免类型错误时会诅咒你的名字。
答案 1 :(得分:0)
我怀疑你的问题与优化器有什么关系。至少在您的示例中,对于所有三个atttributes,VALUE都设置为ENAME。这对于“name”属性来说很好,但对于“sal”来说,它应该是SAL。对于“mgr”,我不知道,因为你的例子没有提供足够的信息。
我还建议删除“|| ename”部分,再次假设优化器不是问题。
最后,如果EMPNO是EMP上的主键,则将UNION更改为UNION ALL。 UNION尝试将结果减少到唯一行,如果它们在ID,ATTR上已经是唯一的,则这是不必要的处理。
重写视图,然后“从ETR中选择*,其中ATTR ='sal'”并确认您所看到的实际上是工资。这应该允许你为没有问题的sal做to_number(ATTR)。