Sql 影响连接的Where子句
愚蠢的提问时间。甲骨文10g where子句是否可能影响联接 我有一个如下形式的查询:Sql 影响连接的Where子句,sql,oracle,oracle10g,Sql,Oracle,Oracle10g,愚蠢的提问时间。甲骨文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_n
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_值是一个VARCHAR2字段,它在某些行中存储了一个ID,而不是文本。(是的,糟糕的设计——但我继承了一些东西,无法改变)
问题在于联合。如果省略where子句,则查询有效并返回行。但是,如果我添加where子句,在pd.product\u value=to\u number(p.product\u value)连接条件上会出现“invalid number”错误
显然,当p.product_值字段中包含非数字的行被合并时,会发生“无效数字”错误。但是,我的问题是如何选择这些行?如果连接成功而没有外部where子句,那么外部where子句不应该只从连接结果中选择行吗?似乎发生的事情是where子句正在影响连接的行,尽管连接在内部查询中
我的问题有意义吗?我想您的
to\u编号(p.product\u值)
仅适用于具有有效产品名称的行
发生的情况是,在where
子句之前应用了join
,导致to_number
函数失败
您需要做的是将您的产品名称(如“%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));
它会影响生成的计划
表的实际连接顺序(以及过滤顺序)不是由您编写查询的顺序决定的,而是由表上的统计信息决定的
在一个版本中,附带生成的计划意味着“坏”行永远不会得到处理;因为前面的连接将结果集过滤到一个从未连接的点
WHERE
子句的引入意味着ORACLE现在认为不同的联接顺序更好(因为按产品名称过滤需要特定的索引,或者因为它将数据缩小了很多,等等)
这个新的顺序意味着“坏”行在过滤掉它们的联接之前得到处理
在查询之前,我会尽力清理数据。可能通过创建一个派生列,其中的值已转换为数字,或者如果无法转换,则保留为NULL
您还可以使用“解释计划”查看查询中生成的不同计划。简短回答:是
长答案:查询引擎可以随意重写查询,只要返回相同的结果。它可以使用所有查询来生成最高效的查询
在这种情况下,我猜有一个索引包含了您想要的内容,但它不包含产品名称,当您将其添加到where子句中时,该索引不被使用,而是在扫描中同时测试两个条件,从而导致您的错误
这在您的加入条件中确实是一个错误,您不应该使用to_number,除非您确定它是一个数字。有关更多背景信息(以及非常好的阅读),我建议阅读Jonathan Gennick的
基本上,问题在于Oracle可以自由地以任何顺序计算谓词。因此,可以自由地将product\u name
谓词推(或不推)到子查询中。可以按任意顺序计算联接条件。因此,如果Oracle碰巧选择了一个查询计划,在将应用于
编号之前过滤掉非数字的产品_值
行,则查询将成功。如果在筛选出非数字的产品\u值
行之前,选择了一个将应用于\u编号
的计划,您将得到一个错误。当然,它也可能会成功返回前N行,然后在尝试获取第N+1行时会出现错误,因为第N+1行是它第一次尝试将to_number
谓词应用于非数字数据
除了修复数据模型之外,您还可以向查询中添加一些提示,强制Oracle计算谓词,以确保在应用to_number
谓词之前过滤掉所有非数字数据。但一般来说,以一种迫使优化器总是以“正确”的顺序计算事物的方式完全提示查询有点困难。您可以给它一点空闲时间(pd.product\u值)=p.product_value
。这可能有帮助,也可能没有帮助-没有任何东西可以阻止Oracle尝试首先计算to_编号
。