前言
EXISTS 和 NOT EXISTS 是 SQL 中用于存在性判断的谓词,返回布尔值(TRUE/FALSE),常用于 WHERE 子句中过滤数据
使用
准备两张表
sql
-- public.campus_device definition
-- Drop table
-- DROP TABLE public.campus_device;
CREATE TABLE public.campus_device (
id int8 NOT NULL,
create_id int8 NULL,
create_time timestamp NULL,
dr int4 NULL,
last_modify_id int8 NULL,
last_modify_time timestamp NULL,
tenant_id int8 NULL,
"version" int4 NULL,
device_name varchar(255) NULL,
mac varchar(255) NULL,
org_id int8 NULL,
product_id int8 NULL,
saas_device_id int8 NULL,
verify_code varchar(255) NULL,
address varchar(255) DEFAULT NULL::character varying NULL,
createid int8 NULL,
devicename varchar(255) NULL,
orgid int8 NULL,
productid int8 NULL,
saasdeviceid int8 NULL,
verifycode varchar(255) NULL,
CONSTRAINT campus_device_pkey PRIMARY KEY (id)
);
以及
sql
-- public.campus_room_user_rel definition
-- Drop table
-- DROP TABLE public.campus_room_user_rel;
CREATE TABLE public.campus_room_user_rel (
id int8 NOT NULL,
room_id int8 NULL,
user_id int8 NULL,
create_id int8 NULL,
create_time timestamp DEFAULT CURRENT_TIMESTAMP NULL,
last_modify_id int8 NULL,
last_modify_time timestamp NULL,
"version" int4 DEFAULT 0 NULL,
dr int4 DEFAULT 0 NULL,
tenant_id int8 NULL,
admin_status varchar NULL,
user_authored_type varchar NULL,
createid int8 NULL,
adminstatus varchar(255) NULL,
roomid int8 NULL,
userauthoredtype varchar(255) NULL,
userid int8 NULL,
bed_number int4 NULL,
CONSTRAINT campus_room_user_rel_pkey PRIMARY KEY (id)
);
语法使用
sql
-- EXISTS:子查询有结果返回 TRUE
SELECT * FROM 表A WHERE EXISTS (SELECT 1 FROM 表B WHERE 表B.id = 表A.id);
-- NOT EXISTS:
子查询无结果返回 TRUE
SELECT * FROM 表A WHERE NOT EXISTS (SELECT 1 FROM 表B WHERE 表B.id = 表A.id);
核心特点
只关心"有没有",不关心"是什么":子查询中写 SELECT 1、SELECT * 或 SELECT 字段名 效果完全一样,因为 EXISTS 只判断是否返回了行,不关心具体返回值。
短路评估:找到第一条匹配记录就立即停止扫描,性能优于 COUNT(*) > 0。
相关子查询:子查询通常依赖外层查询的字段做关联条件,对外层每一行都会执行一次子查询。 执行流程
以 NOT EXISTS 为例:
外层查询取出一行记录
将该行字段值代入子查询执行
子查询返回空集 → NOT EXISTS 为 TRUE → 该行保留
子查询返回至少一行 → NOT EXISTS 为 FALSE → 该行过滤掉
重复以上步骤直到外层遍历完
与 NOT IN 的关键区别
| 表对比维度 | NOT EXISTS | NOT IN |
|---|---|---|
| NULL 处理 | 不受 NULL 影响,安全 | 子查询含 NULL 时整个条件可能返回空集 |
| 执行方式 | 逐行检查,找到即停 | 需先加载完整子查询结果集再逐一比对 |
| 性能 | 通常更优,尤其大数据量场景 | 子查询结果集大时可能全表扫描 |
| ⚠️ 如果子查询字段允许 NULL 且未加 IS NOT NULL 过滤,禁止使用 NOT IN,必须用 NOT EXISTS。 | ||
| 常见应用场景 | ||
| 查找有关联数据的记录(EXISTS):如"查询有订单的客户" | ||
| 查找没有关联数据的记录(NOT EXISTS):如"查询未分配班级的学生" | ||
| 防重复插入:插入前判断记录是否已存在 | ||
| 替代 COUNT(*) > 0 做存在性判断 |
语法查询
sql
SELECT c.*
FROM campus_room c
WHERE c.tenant_id = 2031967244505841665
AND c.dr = 0
AND NOT EXISTS (
SELECT 1
FROM campus_room_user_rel l
WHERE l.room_id = c.id and l.dr = 0
);
select * from campus_room c where c.id= 2075450553046925314 and
exists (select 1 from campus_room_user_rel u where c.id = u.room_id );
总结
数据库中的EXISTS和not EXISTS在某种情况下查询更优