愚蠢的提问时间。 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子句正在影响哪些行被连接,尽管连接在内部查询中。
我的问题有意义吗?
答案 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
谓词之前过滤掉所有非数字数据。但总的来说,以一种强制优化器始终以“正确”的顺序评估事物的方式完全提示查询是有点挑战性的。