Sql 否定任意where子句条件(包括null测试)

Sql 否定任意where子句条件(包括null测试),sql,oracle,null,where-clause,Sql,Oracle,Null,Where Clause,我想有效地检查一个表是否包含匹配和不匹配的行,条件是任意的 在Oracle中,这几乎是可行的: 问题在于可怕的空值。让我们假设is name='Aaron'和is age=21。查询将正确识别年龄不等于21岁的任何Aaron,但无法识别年龄为null的任何Aaron 这是一个正确的解决方案,但对于一个有数百万条记录的表,这可能需要一段时间: select ( select count(*) from people where (<condition A>) ) - ( s

我想有效地检查一个表是否包含匹配和不匹配的行,条件是任意的

在Oracle中,这几乎是可行的:

问题在于可怕的空值。让我们假设is name='Aaron'和is age=21。查询将正确识别年龄不等于21岁的任何Aaron,但无法识别年龄为null的任何Aaron

这是一个正确的解决方案,但对于一个有数百万条记录的表,这可能需要一段时间:

select (
  select count(*) from people
  where (<condition A>)
) - (
  select count(*) from people
  where (<condition A>)
  and (<condition B>)
) from dual;
-- returns zero if all rows that match <condition A> also match <condition B>
-- (correct, but it is s l o w...)
不幸的是,这两种情况将是任意的、复杂的、不断变化的,并且通常超出我的控制范围。它们是从应用程序的持久性框架中通过用户搜索生成的,当我们试图让索引与用户保持同步时,很多时候它们会导致大表扫描,这就是为什么第一个带有exists子句的查询比第二个查询快得多的原因-它可以在找到一个匹配的记录后立即停止,而且它不需要做两次单独的扫描


我怎样才能有效地做到这一点,而不需要对空值进行进一步分析呢?

如果条件完全是任意的,我认为你做不了什么。是否可以根据某些规则在某一点重新写入条件

我相信如果你这样做:

... where not (age = 21) ....
... where not (age = 21 and age is not null) ....
这在内部转化为:

... where (age != 21) ...
... where (age != 21 or age is null) ....
您得到的记录太少,因为它与空值不匹配,对吗

但如果你这样做:

... where not (age = 21) ....
... where not (age = 21 and age is not null) ....
这在内部转化为:

... where (age != 21) ...
... where (age != 21 or age is null) ....
然后你会得到预期的结果。对吧?


因此,您可以强制条件中的所有比较都包含一个空测试,无论是以。。。或者x为空或者。。。并且x不为null?

假设您的表具有主键id,一种可能的方法是:

select count(*)
from people p1
left join people p2
  on (p1.id = p2.id
  and (p2.<condition A>)
  and (p2.<contition B>))
where p1.<condition A>
  and p2.id IS NULL
您确实需要对每个列名称前面加p1的条件进行一些简单的预处理。或者p2。但这比用您提到的空问题正确地否定条件要容易得多


在任何地方左键连接某个表。。。sometable.id为NULL是一种常用的表达方式,并且sometable中没有满足whatever约束的对应记录,因此我希望一个好的引擎能够在可用索引允许的范围内优化该习惯用法。

如果确实有id字段,请尝试:

从dual中选择count* 哪里有 从人物中选择* 康德a在哪里 和zzz.id不在条件b中的人员的选择id中
;

一种解决方案是首先消除比较参数中的任何空值,即,将字符串附加到值或用该列不可能的值替换空值。关于第一个示例:

select x, y
  from foo
  join bar on bar.a||'x' = foo.a||'x' /* Replace "=" with "<>" for opposite result */
;

现在,至少在Oracle 9.2中,第二个选项更难,因为您必须确保替换值与它替换的列的数据类型相同NVL有点傻,而且它的值超出了列数据类型的精度,例如,编号3为9999,但可以使其与索引一起工作。当然,如果列已经使用了最大精度/长度,则这是不可能的。

如果对于每个可为空的列,您可以得出一个不应该有效的伪值,那么您可以执行以下操作:

select count(*) from dual
where exists (
  select * from (
    select NVL(name, 'No Name') name, NVL(age, -1) age from people
    )
  where (<condition A>)
  and not (<condition B>)
);
您可能希望基于这些表达式创建基于函数的索引


这当然比在运行时解析条件并尝试用NVL表达式替换列名更容易,并且应该具有相同的最终结果。

其中nvl2age_列,age_参数,null?您所说的是正确的。这是一个有趣的方法,但比我希望的要复杂一些。条件可能很复杂,如:woclass='ACTIVITY'或woclass='WORKORDER'和reportdate是的,后处理是一项艰巨的任务。有没有可能修改生成表达式的代码?谢谢。我认为这是避免预处理条件的最快解决方案。在我的测试中,速度与Alex的解决方案相当。