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
。事实上,只要不要将IN
和NOT 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 <> null
是null
,而不是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 <> null
永远不会是真的。这就是您的 not in
查询没有返回任何行的原因。
【讨论】:
以上是关于Oracle“NOT IN”没有返回正确的结果?的主要内容,如果未能解决你的问题,请参考以下文章
oracle_not exists和not in的用法和区别