【问题标题】:speed up a not-exists query加快不存在的查询
【发布时间】:2014-06-30 10:56:36
【问题描述】:

我有一个像这样工作的 oracle 查询:

SELECT * 
FROM VW_REQUIRED r
JOIN VW_ACTUAL a ON a.person_id = r.person_id AND a.target_id = r.target_id

必需是详细说明所有必需培训材料的视图,实际是详细说明给定课程最近学习的视图。这些查询中的每一个都需要不到 2 秒的时间来生成 10k 到 100k 行。

我想做的是这样的:

SELECT *
FROM VW_REQUIRED r
WHERE NOT EXISTS ( SELECT 1 FROM VW_ACTUAL a WHERE a.person_id = r.person_id AND a.target_id = r.target_id) 

而且需要超过 20 秒(我没有让它完成,因为那显然太长了。

所以我决定做一些不同的事情,我将原始的 JOIN 设置为左​​连接,这样它就会向我显示所有必需的培训,并且只有存在时才会显示实际的培训。

这行得通,而且速度仍然非常快。

但我想要一份没有实际培训的课程列表(即我们需要开始工作并接受培训的人......)

当我尝试像

SELECT * 
FROM VW_REQUIRED r
LEFT JOIN VW_ACTUAL a ON a.person_id = r.person_id AND a.target_id = r.target_id
WHERE r.person_id = null

我没有返回任何行。我不确定是否可以过滤掉没有实际结果的行。通常我会使用WHERE NOT EXISTS,但它的性能非常慢(而且我认为我不能在视图上放置索引......)

我已经设法进行了一些可行的更改,但它看起来很老套,我确信有更好的解决方案。

SELECT
  who, where_from, mand, target_id, grace_period, date_taken
FROM (
    SELECT
      r.person_id who,
      r.where_from where_from,
      r.mand mand,
      r.target_id target_id
      r.grace_period grace_period,
      nvl(a.date_taken, to_date('1980/01/01','yyyy/mm/dd')) date_taken
    FROM VW_REQUIRED r
    LEFT JOIN VW_ACTUAL a ON a.person_id = r.person_id AND a.target_id = r.target_id
)
WHERE date_taken = to_date('1980/01/01','yyyy/mm/dd')

【问题讨论】:

  • 这是一个可怕的黑客攻击。 @LajosVerdes 提供的解决方案更干净,甚至应该更快。您是否 100% 确定您尝试过他提出的查询(将 r.person_id 替换为 a.person_id 并使用 IS NULL 而不是 = NULL)?

标签: sql oracle optimization not-exists


【解决方案1】:

也许您的问题来自不存在查询中的错误计划。

您能告诉我们这个查询的计划吗?

SELECT *
FROM VW_REQUIRED r
WHERE NOT EXISTS ( SELECT 1 FROM VW_ACTUAL a WHERE a.person_id = r.person_id AND a.target_id = r.target_id) 

并尝试更改连接算法(/+use_nl(a)/ 或 /+use_hash(a)/)。 我认为这是一个嵌套循环。也许你必须像这样在这个例子中加入一个哈希连接

SELECT *
FROM VW_REQUIRED r
WHERE NOT EXISTS ( SELECT /*+use_hash(a)*/ 1 FROM VW_ACTUAL a WHERE a.person_id = r.person_id AND a.target_id = r.target_id) 

【讨论】:

  • 您拼错了提示(它是 use_hash,而不是 use_ash)。除非绝对必要,否则我也会非常犹豫使用提示;他们将来会回来困扰你。
  • 你说错了。我会纠正它。对于您的第二个断言(使用或不使用提示),我知道 Oralce 公司的官方“规则”(永远不要使用提示,Oracle 比您做得更好)。但在现实生活中,当它变得复杂时,它多次挽救了我们的生命;)
  • 我想解释一下我为什么要做哈希连接。 corsiKa 说两个视图的时间响应小于 2 秒。如果为真,则使用散列连接的不存在查询的时间响应必须小于 4 秒。使用嵌套循环,它可以是瞬时的或无限的!如果我是对的,那么问题就会改变。我们会知道一个解决方案(散列连接),但我们必须了解为什么 Oracle 不想使用它(或者如果我们很懒惰,请保留提示)
【解决方案2】:

我认为您只是混合了表名。你能把最后一个地方改成

a.person_id is null

?

所以你的查询应该是这样的:

SELECT * 
FROM VW_REQUIRED r
LEFT JOIN VW_ACTUAL a ON a.person_id = r.person_id AND a.target_id = r.target_id
WHERE a.person_id is null

或者也许是“旧”方式?

SELECT * 
FROM
VW_REQUIRED r,
VW_ACTUAL a
WHERE  r.person_id = a.person_id(+) 
AND r.target_id = a.target_id(+)
AND a.person_id is null  

【讨论】:

  • 不管怎样,它什么都不显示。
  • 我认为这是问题所在。您只需要 a.person_id 为空的结果(这意味着 VW_ACTUAL 中没有相关记录)
  • 我设法找到了一个可行的选择,但它似乎仍然有点 hacky。我将其添加到我的问题中。
  • 这个查询看起来是正确的。虽然你说它什么也没显示,但这有关键点:1)左连接 2)连接表上的额外条件在连接条件中,而不是 where 子句 3)where 子句在非 null 上测试 null连接表中的列。如果您一无所获,请检查您是否确实有应该匹配的数据和/或对额外条件进行准确计时
  • @LajosVeres 你忘了说你也修复了第二个错误:-)(使用= NULL而不是IS NULL
猜你喜欢
  • 1970-01-01
  • 2013-03-29
  • 2018-11-07
  • 1970-01-01
  • 1970-01-01
  • 2012-11-22
  • 2013-07-29
  • 1970-01-01
相关资源
最近更新 更多