SQL中的IN和NOT IN结果不同

时间:2018-01-05 08:50:41

标签: sql postgresql

我尝试做两个查询,在我看来它应该给我相同的结果,但没有

SELECT count(1)
FROM users
WHERE users.id NOT IN (
    SELECT user_id as id
    FROM users_roles as ur
    WHERE 
        ur.role_id = 10
        OR ur.role_id = 12
    )

我有结果:

 count 
-------
     0
(1 row)

第二次将NOT IN更改为IN并将其换行到外NOT IN

SELECT count(1)
FROM users
WHERE id NOT IN (
    SELECT users.id
    FROM users
    WHERE users.id IN (
        SELECT user_id as id
        FROM users_roles as ur
        WHERE 
            ur.role_id = 10
            OR ur.role_id = 12
        )
)

结果

  count  
---------
 3150136
(1 row)

第一次查询有什么问题?

第二次查询的更多细节:

SELECT count(1)
FROM users
WHERE users.id IN (
    SELECT user_id as id
    FROM users_roles as ur
    WHERE 
        ur.role_id = 10
        OR ur.role_id = 12
    )

 count 
-------
 40320
(1 row)

select count(1) from users;
  count  
---------
 3190466

按用户查询编辑:

database=# \d users_roles
                          Table "public.users_roles"
  Column  |  Type   |                        Modifiers                         
----------+---------+----------------------------------------------------------
 user_id  | integer | 
 role_id  | integer | 
 track_id | integer | 
 id       | integer | not null default nextval('users_roles_id_seq'::regclass)
Indexes:
    "users_roles_pkey" PRIMARY KEY, btree (id)
    "uniq_users_roles" UNIQUE CONSTRAINT, btree (user_id, role_id)
    "uq_users_roles_role_track" UNIQUE CONSTRAINT, btree (role_id, track_id)
Foreign-key constraints:
    "fk_roles_track_id" FOREIGN KEY (track_id) REFERENCES tracks(id)
    "fk_users_roles_roles" FOREIGN KEY (role_id) REFERENCES roles(id)
    "fk_users_roles_users" FOREIGN KEY (user_id) REFERENCES users(id)

并选择

SELECT Count(user_id) as totalusers FROM users_roles as ur WHERE ur.role_id = 10 OR ur.role_id = 12;
     totalusers 
------------
      40320

P.S。

database=# select version();
                                                   version                                                    
--------------------------------------------------------------------------------------------------------------
 PostgreSQL 9.4.14 on x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4, 64-bit

1 个答案:

答案 0 :(得分:4)

这可能是因为user_roles = user_idrole_id = 10时,表格role_id可能为12

第一个查询变为:

SELECT COUNT(1)
FROM users
WHERE users.id NOT IN (id1, id2, id3, NULL, id4, NULL, id5,...)

这里给定的users.id将与列表中的所有值进行比较,并且只有当它不等于上面列表中的任何id值时,它才有资格显示在输出中。

如果列表中存在NULL值,NOT IN会将给定users.id的条件评估为[users.id<> id1users.id <> id2 AND ... users.id<> NULLusers.id<> id5]你期望的是真的但是SQL将其视为FALSE,因为它将users.id<> NULL评估为FALSE,因此将整个表达式评估为FALSE并从结果中排除users.id。这发生在users.id表中的所有其他users,这就是输出中有0个条目的原因。

仅供参考:id = NULL => UNKNOWN以及id<> NULL => UNKNOWN其中UNKNOWN既不是TRUE也不是FALSE(但是在你的问题的背景下,你仍然可以说not TRUE当你期望它是TRUE)因为NULL实际上是“未知”没有任何价值。

为避免这种情况,您可以从内部列表中删除NULL,如下所示:

SELECT count(1)
FROM users
WHERE users.id NOT IN (
SELECT user_id as id
FROM users_roles as ur
WHERE 
    (ur.role_id = 10 OR ur.role_id = 12) AND ur.user_id IS NOT NULL
)

这是NOT IN

但是,使用IN时不会遇到此问题,因为如果列表中存在匹配的非NULL users.id,则它将包含在输出中这就是为什么你在另一个查询中得到40,320条记录的原因。