Oracle“NOT IN”没有返回正确的结果?

Posted

技术标签:

【中文标题】Oracle“NOT IN”没有返回正确的结果?【英文标题】:Oracle "NOT IN" not returning correct result? 【发布时间】:2020-07-15 11:27:11 【问题描述】:

我正在使用 Oracle 中的 NOT IN 函数比较两个共享唯一值的表,但我得到了

select count(distinct CHARGING_ID)   from BILLINGDB201908 where CDR_TYPE='GPRSO'

所有计费 ID 的输出为:521254 --

现在我想在表 BILLINGDB201908 中找到也存在于表 CBS_CHRG_ID_AUG 中的 id

select count(distinct CHARGING_ID)   from BILLINGDB201908 where CDR_TYPE='GPRSO'
AND charging_id IN (select CHARGINGID from CBS_CHRG_ID_AUG);

---结果返回315567计费ID存在BILLINGDB201908也存在于CBS_CHRG_ID_AUG中

现在我想查找 CBS_CHRG_ID_AUG 中不存在但存在 BILLINGDB201908 的计费 ID

select count(distinct CHARGING_ID)   from prmdb.CDR_TAPIN_201908@prmdb where CDR_TYPE='GPRSO'
AND charging_id NOT IN (select CHARGINGID from CBS_CHRG_ID_AUG);

--返回结果0!?我应该得到205687 正是因为521254-315567 = 205687?

【问题讨论】:

【参考方案1】: 如果子查询中的任何值为NULL

NOT IN 返回无行。因此,我强烈推荐NOT EXISTS

SELECT count(distinct CHARGING_ID)   
FROM prmdb.CDR_TAPIN_201908@prmdb  ct
WHERE CDR_TYPE = 'GPRSO' AND
      NOT EXISTS (SELECT 1
                  FROM CBS_CHRG_ID_AUG ccia
                  WHERE ccia.charging_id = ct.charging_id
                 );

我还建议将您的第一个查询更改为EXISTS。事实上,只要不要将INNOT IN 与子查询一起使用,就不会出现这个问题。

【讨论】:

感谢 Gordon,您的查询也可以正常工作,但我不明白子查询中的这一部分? SELECT 1. @OsamaAl-Banna。 . . EXISTS 只是检查是否存在一行。 1 为行提供一列以避免语法错误。【参考方案2】:

缺失的记录具有空值 CHARGINGID。

请尝试选择 CHARGINGID 为空还是不为空

【讨论】:

感谢 CBS_CHRG_ID_AUG 中只有 1 个 NULL 行具有 NULL 值,一旦删除查询工作正常并返回正确的结果。【参考方案3】:

我会推荐not exists 而不是not in;它是null-safe,而且通常更高效:

select count(distinct charging_id)   
from billingdb201908 b
where 
    b.cdr_type = 'gprso'
    and not exists (select 1 from cbs_chrg_id_aug a where a.chargingid = b.chargingid)

【讨论】:

【参考方案4】:

您可以使用LEFT OUTER JOIN 获取此列表。

返回 CBS_CHRG_ID_AUG 中不存在但存在 BILLINGDB201908 的计费 ID 列表的 SQL -

select count(distinct CHARGING_ID)   
from prmdb.CDR_TAPIN_201908@prmdb a 
left join CBS_CHRG_ID_AUG b on a.CHARGING_ID = b.CHARGINGID 
where a.CDR_TYPE='GPRSO' and b.CHARGINGID is null;

【讨论】:

【参考方案5】:

当子查询键可能包含空值时,not in 有两个危险:

    如果实际上有一个空值,您可能不会得到您期望的结果(如您所见)。数据库实际上是正确的,尽管 SQL 历史上没有人预料到这个结果。 即使填充了所有键值,如果键列可能为空(如果未定义为not null),则数据库必须检查是否存在空值,因此查询是仅限于低效的逐行过滤操作,这对于大容量可能会造成灾难性的影响。 (这在历史上是正确的,尽管现在有 Null-aware anti-join,所以性能问题可能不会那么严重。)
create table demo (id) as select 1 from dual;

select * from demo;

        ID
----------
         1
create table table_with_nulls (id) as (
  select 2 from dual union all
  select null from dual
);

select * from table_with_nulls;

        ID
----------
         2
select d.id
from   demo d
where  d.id not in
       ( select id from table_with_nulls );

no rows selected
select d.id
from   demo d
where  d.id not in
       ( select id from table_with_nulls
         where  id is not null );

        ID
----------
         1

原因是1 <> nullnull,而不是false。如果你用一个固定列表代替not in 子查询,它将是:

select d.id
from   demo d
where  d.id not in (2, null);

其实和

是一样的
select d.id
from   demo d
where  d.id <> 2 and d.id <> null;

显然d.id &lt;&gt; null 永远不会是真的。这就是您的 not in 查询没有返回任何行的原因。

【讨论】:

以上是关于Oracle“NOT IN”没有返回正确的结果?的主要内容,如果未能解决你的问题,请参考以下文章

NOT IN vs IN 不返回免费结果

oracle_not exists和not in的用法和区别

mysql中not in怎么使用

oracle子查询中not in后面不能为null值的理解

转 [ORACLE]详解not in与not exists的区别与用法(not in的性能并不差!)

Oracle not in查不到应有的结果(NULLINEXISTS详解)