甲骨文。防止合并子查询和主查询条件

Posted

技术标签:

【中文标题】甲骨文。防止合并子查询和主查询条件【英文标题】:Oracle. Preventing merge subquery and main query conditions 【发布时间】:2016-05-24 18:53:56 【问题描述】:

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

例如:

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 阻止这种优化?

附言我想得到与

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

但是,没有 CTE。

【问题讨论】:

【参考方案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 中描述的那样。现在解决这个问题,否则将来的开发人员必须编写复杂的查询以避免类型错误时会诅咒你的名字。

【讨论】:

", rownum -- 添加 ROWNUM 以保证类型安全。" -惊人的!这就像一个魔术 这不是真正的解决方案,而只是学生任务的一部分。在此任务中,学生尝试使用 Object-Object_types-Attributes-Params 将普通表转换为 EAV 和类似的元模型,并尝试在此数据视图上编写查询。 另一方面,在基于 PostgreSQL 的真实项目中,我也遇到了类似的问题。在我提交的一张表中,用户可以在其中放置指向节点的链接。有些链接可以转换为数字并链接到内部表,但其他链接是 URL,所以我不能转换它。【参考方案2】:

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

【讨论】:

我不认为CREATE VIEW ... 是真正的问题,这只是用于创建测试的代码。最后一个查询很重要。 “至少在您的示例中,所有三个属性的 VALUE 都设置为 ENAME。” - 仅为此示例添加。 ENAME 添加到属性名称只是为了防止其他 Oracle 优化,其中 DB 通过添加“(null is not null)”过滤器跳过相同的子查询。 执行“select * from EAV where ATTR = 'sal' and to_number(value) > 5000” - 是这个问题的初始问题。通过设计应用过滤器时的所有数据库(在我们的例子中 ATTR = 'sal' 和 to_number(value) > 5000 )对过滤器的所有部分和所有行执行此操作,即使执行过滤器的一部分也不会对结果产生任何更改。例如,过滤器“(1 = 1或very_heavy_function())”将为每一行执行very_heavy_function()。在我们的例子中,to_number 将执行到所有行,但必须只执行到 ATTR = 'sal'。

以上是关于甲骨文。防止合并子查询和主查询条件的主要内容,如果未能解决你的问题,请参考以下文章

甲骨文。无法理解 FOR 如何与子查询 SELECT INTO 一起使用

oracle学习之基本查询和条件过滤,分组函数使用

如何在查询后合并字段,我知道 CONCAT,但不是这样 [重复]

如何在 SQL 中编写 if else if 条件 |甲骨文 |

oracle 的初始情况(子查询的东西)

为了比较,在没有 IN 条件的情况下重写 Oracle 查询/更新