神谕。防止合并子查询和主查询条件

Oracle. Preventing merge subquery and main query conditions

我有一个很大的实体属性值,如 table。我尝试使用子查询 select 来自此 table 的一些行,然后过滤行。在这种情况下如何防止合并子查询和主查询?

例如:

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 通过向子查询 1 和 3 添加过滤器“(空不为空)”来优化下一个查询

在子查询中,我 select 所有具有属性 'sal%' 的行,然后在主查询中过滤它:

select *
FROM (select id,value from EAV where attr like 'sal%')
WHERE to_number(value) > 5000;

此查询失败 导致优化器将子查询与外部查询合并。合并数据库后,尝试将 to_number 应用于列 "value" 中的所有值,但其中一些值具有字符串值。 提示阻止此优化?

p.s。我想得到与

相同的结果
WITH t as (
   select /*+ materialize */ id,value
   from eav
   where attr like 'sal%') 
select * from t where to_number(value) > 5000;

但是,没有 CTE。

我怀疑你的问题真的与优化器有关。至少在您的示例中,所有三个属性的 VALUE 都设置为 ENAME。 "name" 属性没问题,但 "sal" 可能应该是 SAL。对于 "mgr",我不知道,因为您的示例没有提供足够的信息。

我还建议删除“||ename”部分,再次假设优化器不是问题所在。

最后,如果 EMPNO 是您在 EMP 上的主键,请将 UNION 更改为 UNION ALL。 UNION 尝试将结果缩减为唯一行,如果它们在 ID、ATTR 上已经是唯一的,则这是不必要的处理。

修改视图,然后 "select * from EAV where ATTR = 'sal'" 并确认您看到的确实是薪水。那应该可以让你毫无问题地为 sal 做 to_number(ATTR)。

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;

还有其他方法可以做到这一点,但其中 none 是可靠的。不要为简单的内联视图、常见的 table 表达式、CASE、谓词排序或提示而烦恼。那些常用的方法都不靠谱,我都见过失败的。


最好的长期解决方案是更改 EAV table,使每种类型都有不同的列,如我在 中所述。立即修复此问题,否则未来的开发人员在必须编写复杂查询以避免类型错误时会诅咒你的名字。