【问题标题】:MINUS vs NOT in where clause在 where 子句中的 MINUS 与 NOT
【发布时间】:2016-12-08 22:11:56
【问题描述】:

我有一个从单个表中检索数据的查询。但是查询使用MINUS 子句,如下所示:

SELECT field1, field2
FROM tab
WHERE field3 = 'a'
AND field4 = 'b'
    MINUS
SELECT field1, field2
FROM tab
WHERE field5 = 'c'
AND field6 = 'd'

因为这是从同一个表中选择的,所以我试图重写它以摆脱MINUS 子句。我认为这样的事情应该可以工作:

SELECT DISTINCT field1, field2
FROM tab
WHERE field3 = 'a'
AND field4 = 'b'
AND NOT (field5 = 'c'
AND field6 = 'd')

我的理由是,如果MINUS 子句排除了第二个查询中的记录,那么将WHERE 子句包装在AND NOT 中应该会删除相同的记录。 MINUS 子句也消除了重复项,这就是我在选择中添加DISTINCT 的原因。但是问题是我的查询返回的记录比原始记录多更多

我在这里错过了什么?

【问题讨论】:

  • 第二个查询只检查单个行。 WHERE 子句适用于被访问的每个单独的行。没有参考检查表中是否存在 other 行。获取第一个查询返回的结果类型的规范方法是基于集合的运算符(MINUS 集合操作)、具有相关子查询的 NOT EXISTS 谓词或反连接模式。为了获得与 MINUS 集合运算符相同的折叠行为,我们需要添加一个 GROUP BY 子句。另一种需要 GROUP BY 的方法是对聚合进行 HAVING 检查。

标签: sql database oracle


【解决方案1】:

考虑这两行:

1、2、a、b、x、y

1、2、u、v、c、d

MINUS 操作不会返回对 (1, 2),但您的查询会。 c, d 值可能以相同的 1, 2 出现,但与 a, b 出现在不同的行中

根本区别在于 MINUS 在设置的级别上运行,而您的 NOT 条件一次仅适用于一行(同一行与其他列中的“必需”值)。

现在:您可以提高查询效率(尽管您无法避免两次读取基表)。使用 NOT IN 条件:

select field1, field2 from tab where field3 = 'a' and field4 = 'b'
and    (field1, field2) not in 
           (select field1, field2 from tab where field5 = 'c' and field6 = 'd');

注意(参见下面 spencer7593 的评论):在所有可能存在 NULL 的情况下,NOT IN 不是一个好的解决方案。相反,应该使用 NOT EXISTS 条件。我不会详细说明,因为它似乎超出了所提问题的范围(这就是为什么“NOT”解决方案与“MINUS”解决方案不同)。

【讨论】:

  • 当心子查询为 field1 和 field2 返回 NULL 值。考虑在我们执行以下操作后 NOT IN 如何评估:INSERT INTO tab (field1,field2,field5,field6) VALUES (NULL,NULL,'c','d')
  • @spencer7593 - 当然。我会添加一个注释来解释。
  • 正如我在回答中所展示的,没有必要读取基表两次。
  • @DuduMarkovitz - 对。我应该换一种说法:“尽管这样你不会避免读取基表两次。”您提供的分组解决方案(以及对原始帖子的评论中提到的 Spencer)优于我的。我更关注 OP 的问题,即“为什么他的第二个查询比 MINUS 解决方案产生更多的结果” - 但为了获得最佳解决方案,他应该查看您的答案。 (实际上我发布的内容甚至都没有区分结果,所以这并不是 OP 想要的。)
  • 仅作记录 - 我的解决方案在评论之前 25 分钟 :-)
【解决方案2】:

在此解决方案中,表仅被引用一次。

select      field1,field2

from        tab

where       (field3,field4) in (('a','b'))
        or  (field5,field6) in (('c','d'))

group by    field1,field2

having      max(case when (field5,field6) in (('c','d')) then 1 else 0 end) = 0
;

select      field1,field2

from        tab

group by    field1,field2

having      max(case when (field3,field4) in (('a','b')) then 1 else 0 end) = 1
        and max(case when (field5,field6) in (('c','d')) then 1 else 0 end) = 0
;

【讨论】:

  • 这可能比使用 select distinct 和 set 运算符更有效。
  • DISTINCT 不能在这里使用,因为 HAVING 逻辑。我希望 GROUP BY 和 DISTINCT 具有相同的实现和性能。
  • 。 .我在原始问题中指的是select distinct
猜你喜欢
  • 2016-04-10
  • 2013-04-17
  • 2016-01-09
  • 1970-01-01
  • 1970-01-01
  • 2020-09-25
  • 1970-01-01
  • 2012-05-16
  • 1970-01-01
相关资源
最近更新 更多