2012-04-10 45 views
5

愚蠢的问题时间。 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)连接条件中收到“无效编号”错误。

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

我的问题有意义吗?

+0

你可以给它一点点'to_char(pd.product_value)= p.product_value'。 – briantyler 2012-04-10 15:27:34

回答

1

简答:是的。

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

在这种情况下,我猜想有一个索引涵盖了你想要的东西,但它不包括产品名称,当你将其添加到where子句时,索引不被使用,而是改为有一个扫描在两个条件同时进行测试,因此你的错误。

在连接条件中,这实际上是一个错误,除非您确定它是一个数字,否则不应该使用to_number。

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)); 
+0

这可能会也可能不会有帮助 - 没有什么能阻止Oracle首先尝试评估“to_number”。 – 2012-04-11 01:38:05

2

它会影响系统产生的计划。

表格加入(并过滤)的实际顺序不是由您编写查询的顺序决定的,而是由表格上的统计信息决定的。

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

WHERE子句的引入意味着ORACLE现在认为不同的连接顺序更好(因为根据产品名称进行筛选需要特定的索引,或者因为它将数据缩小了很多,等等)。

这个新订单意味着'坏'行在联结过滤之前得到处理。


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

您还可以使用EXPLAIN PLAN查看正在查询的不同计划。

0

欲了解更多背景(和一个非常好的阅读),我建议阅读乔纳森根尼克的Subquery Madness

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

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