0

我有一个很大的实体属性值,比如一张表。我尝试使用子查询从该表中选择一些行,然后用行过滤。在这种情况下如何防止合并子查询和主查询?

例如:

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 添加过滤器“(null is not null)”来优化下一个查询

在子查询中,我选择具有属性“sal%”的所有行,然后在主查询中对其进行过滤:

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

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

ps我想得到相同的结果

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

但是,没有 CTE。

4

2 回答 2

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 表,使每种类型都有不同的列,正如我在这个答案中所描述的那样。现在解决这个问题,否则将来的开发人员在必须编写复杂的查询以避免类型错误时会诅咒你的名字。

于 2016-05-24T23:04:03.480 回答
0

我怀疑你的问题真的与优化器有关。至少在您的示例中,所有三个属性的 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) 没有问题。

于 2016-05-24T22:18:47.927 回答