【问题标题】:Oracle "NOT IN" not returning correct result?Oracle“NOT IN”没有返回正确的结果?
【发布时间】: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?

【问题讨论】:

    标签: sql database oracle subquery


    【解决方案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 有两个危险:

        1. 如果实际上有一个空值,您可能不会得到您期望的结果(如您所见)。数据库实际上是正确的,尽管 SQL 历史上没有人预料到这个结果。
        2. 即使填充了所有键值,如果键列可能为空(如果未定义为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 查询没有返回任何行的原因。

        【讨论】:

          猜你喜欢
          • 2023-03-14
          • 1970-01-01
          • 2014-09-13
          • 2012-08-01
          • 2014-05-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多