【问题标题】:oracle query with inconsistent resultsoracle查询结果不一致
【发布时间】:2010-04-22 15:10:04
【问题描述】:

我遇到了一个非常奇怪的问题,我有一个复杂的视图,当我在特定列上查询时返回不正确的数据。
举个例子:

select empname
       , has_garnishment 
from timecard_v2 
where empname = 'Testerson, Testy';

这将返回单个结果 'Testerson, Testy', 'N'

但是,如果我使用查询:

select empname
       , has_garnishment 
from timecard_v2 
where empname = 'Testerson, Testy'
and has_garnishment = 'Y';

这会返回单个结果 'Testerson, Testy', 'Y'

第二个查询应该返回第一个查询的子集,但它返回不同的答案。

当我使用查询时:

select empname
       , has_garnishment 
from timecard_v2 
where empname = 'Testerson, Testy'
and has_garnishment = 'N';

我没有得到任何结果

我已经剖析了视图并确定视图定义的这一部分是问题出现的地方,即使我采用 sql 定义并将其作为直接查询运行(注意,我删除了所有 select 子句为了清楚起见,除了感兴趣的部分,在完整查询中所有连接表都是必需的):


SELECT 
  e.fullname empname ,
  NVL2(ded.has_garn, 'Y', 'N') has_garnishment
FROM timecard tc ,
  orderdetail od ,
  orderassign oa ,
  employee e ,
  employee3 e3 ,
  customer10 c10 ,
  order_misc om,
  (SELECT COUNT(*) has_garn,
    v_ssn
  FROM deductions
  WHERE yymmdd_stop                             = 0
  OR (LENGTH(yymmdd_stop)                       = 7
  AND to_date(SUBSTR(yymmdd_stop, 2), 'YYMMDD') > sysdate)
  GROUP BY v_ssn
  ) ded
WHERE oa.lrn(+) = tc.lrn_order
AND om.lrn(+)   = od.lrn
AND od.orderno  = oa.orderno
AND e.ssn       = tc.ssn
AND c10.custno  = tc.custno
AND e.lrn       = e3.lrn
AND e.ssn       = ded.v_ssn(+)

关于“ded”子查询的定义有一点需要注意。 v_ssn 字段是 deductions 表上的虚拟字段。

我不是 DBA,我是软件开发人员,但我们最近失去了 DBA,而新的 DBA 仍在加快速度,所以我正在尝试调试这个问题。话虽如此,请比其他预言机专家更彻底地解释一下。

谢谢

【问题讨论】:

  • DED 子查询似乎可以使用一些格式和 cmets 来帮助使其更受支持。但我怀疑我在阅读 SQL 时遇到的麻烦与问题有关。为了获得更多信息,请尝试查看 SQL 语句并添加“empname = 'Testerson, Testy'”限制器。你得到了什么结果?
  • 我有兴趣查看两个查询的执行计划给出不一致的结果。在 SQLPlus 中,输入 set autotrace traceonly explain 然后执行查询。
  • 我想知道当您输入第一个查询并添加 had_garnishment = 'N' 时会得到什么
  • 戴夫,我会尽快获取执行计划并将其添加到问题中。 Jay 和 MJB,我在问题中添加了您问题的答案。谢谢大家
  • 只是为了确保我理解,当 ded 子查询外连接但为空时,NVL2 返回 Y,对吗?当在正确的范围内有一些扣除时,N?还有一个请求,你能做'select has_garnishment,count(*) from timecard_v2 group by has_garnishment'吗?我还在想。

标签: sql oracle


【解决方案1】:

原来问题是索引冲突。在构建 v_ssn 虚拟列的列上有一个旧索引。我删除了该索引,查询开始按预期运行。我仍然担心该索引如何影响查询,但至少我的直接问题得到了解决。

感谢您的帮助!

【讨论】:

  • 那么,v_ssn 是基于非确定性函数的吗?这是我能想到的唯一解释,删除索引可能会改变查询结果。
【解决方案2】:

首先,您有一个连接“AND od.orderno = oa.orderno”,它将否定“oa.lrn(+) = tc.lrn_order”上的外部连接。 IE,如果 tc.lrn_order 没有在 oa 上找到匹配项,则外部连接表示仍返回一行,但会有一个空 orderno,这将导致与 od 的任何匹配失败

其次,虚拟列的定义可能是相关的。如果删除索引解决了问题,则表明之前的计划使用了索引。

第三,ded 子查询似乎有点笨拙。从表面上看,

SELECT COUNT(*) FROM tab WHERE COL=:val

看起来很像

SELECT cnt FROM (SELECT COL, COUNT(*) FROM tab GROUP BY COL) WHERE COL=:val

但如果 :val 在“tab”中不存在,第一个将返回计数 0,第二个将不返回任何行(并且使用外连接,将返回 NULL)。

如果 COL 上有可用的索引,那么选项 1 可能很有吸引力。我怀疑甲骨文在某处将后者重写为前者,如果您只是显示了 has_garn 值而不是 NVL2,您会看到 0 而不是 null。

该错误的替代方案可能是这样的:

如果 v_ssn 基于 func(col),那么 Oracle 可能(错误地)假设 func(COL) 必须为 null,如果 COL 为 null。如果大部分 COL 为空,它可能会假设使用 COL 上的索引来查找非空行会使其处理更小且更有效的行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-07
    相关资源
    最近更新 更多