5

愚蠢的提问时间。甲骨文 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 子句正在影响连接的行,尽管连接在内部查询中。

我的问题有意义吗?

4

4 回答 4

2

它会影响生成的计划。

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

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

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

这个新顺序意味着“坏”行在过滤掉它们的连接之前得到处理。


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

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

于 2012-04-10T15:07:02.253 回答
1

简短的回答:是的。

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

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

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

于 2012-04-10T15:12:22.267 回答
0

有关更多背景信息(以及非常好的阅读内容),我建议阅读 Jonathan Gennick 的Subquery Madness

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

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

于 2012-04-10T15:22:40.923 回答
0

我猜你to_number(p.product_value)只适用于有效的行product_name

发生的情况是您的join被应用在您的where子句之前导致to_number功能失败。

您需要做的是包含您的product_name like '%prototype%'asJOIN子句,如下所示:

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));
于 2012-04-10T15:04:49.590 回答