Where子句影响join

时间:2012-04-10 14:57:55

标签: sql oracle oracle10g

愚蠢的提问时间。 Oracle 10g。

where子句是否可以影响连接?

我的查询形式为:

select * from
(select product, product_name from products p
join product_serial ps on product.id = ps.id
join product_data pd on pd.product_value = to_number(p.product_value)) product_result
where product_name like '%prototype%';

显然这是一个人为的例子。没有必要显示表格结构,因为它是所有想象的。不幸的是,我无法显示真正的表结构或查询。在这种情况下,p.product_value是一个VARCHAR2字段,在某些行中有一个存储在其中的ID而不是文本。 (是的,糟糕的设计 - 但我继承并且无法改变)

问题在于加入。如果我省略where子句,查询将起作用并返回行。但是,如果我添加where子句,则会在pd.product_value = to_number(p.product_value)连接条件中出现“invalid number”错误。

显然,当连接的行包含p.product_value字段中的非数字时,会发生“无效数字”错误。但是,我的问题是这些行是如何被选中的?如果连接成功而没有外部where子句,那么外部where子句不应该只从连接的结果中选择行吗?看来正在发生的是where子句正在影响哪些行被连接,尽管连接在内部查询中。

我的问题有意义吗?

4 个答案:

答案 0 :(得分:2)

它会影响生成的计划。

表连接(以及如此过滤)的实际顺序不是由您编写查询的顺序决定的,而是由表的统计信息决定。

在一个版本中,同时生成的计划意味着“坏”行永远不会得到处理;因为前面的连接将结果集过滤到了它们永远不会被连接的点。

WHERE子句的引入意味着ORACLE现在认为不同的连接顺序更好(因为按产品名称过滤需要某个索引,或者因为它会大大缩小数据等)

这个新订单意味着在过滤它们的连接之前处理'坏'行。


我会尽力在查询之前清理数据。可能是通过创建一个派生列,其中值已经转换为数字,或者如果不可能则保留为NULL。

您还可以使用EXPLAIN PLAN查看从查询中生成的不同计划。

答案 1 :(得分:1)

简答:是的。

答案很长:查询引擎可以随意重写您的查询,只要它返回相同的结果即可。所有查询都可用于生成最有效的查询。

在这种情况下,我猜测有一个索引涵盖了你想要的东西,但是它没有涵盖产品名称,当你将它添加到where子句时,索引没有被使用,而是有了扫描同时测试两个条件,从而导致错误。

你的加入条件确实是一个错误,你不应该使用to_number,除非你确定它是一个数字。

答案 2 :(得分:0)

我猜您的to_number(p.product_value)仅适用于有效product_name的行。

join子句之前,您的where会被应用,从而导致to_number功能失败。

您需要做的是将product_name like '%prototype%'作为JOIN条款包含在内:

select * from
(select product, product_name from products p
join product_serial ps on product.id = ps.id
join product_data pd on product_name like '%prototype%' AND
     pd.product_value = to_number(p.product_value));

答案 3 :(得分:0)

对于更多背景(以及非常好的阅读),我建议阅读Jonathan Gennick的Subquery Madness

基本上,问题是Oracle可以按任意顺序自由评估谓词。因此可以将product_name谓词推送(或不推送)到子查询中。可以按任何顺序自由评估连接条件。因此,如果Oracle恰好选择了一个查询计划,在它应用product_value之前过滤掉非数字to_number行,则查询将成功。如果在过滤掉非数字to_number行之前选择了应用product_value的计划,则会出现错误。当然,它也可能成功返回前N行,然后当你尝试获取行N + 1时会出现错误,因为第N + 1行是第一次尝试应用{{ 1}}谓词到非数字数据。

除了修复数据模型之外,您还可以在查询中添加一些提示,以强制Oracle评估谓词,以确保在应用to_number谓词之前过滤掉所有非数字数据。但总的来说,以一种强制优化器始终以“正确”的顺序评估事物的方式完全提示查询是有点挑战性的。