惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Docker
U
Unit 42
Google DeepMind News
Google DeepMind News
B
Blog RSS Feed
S
SegmentFault 最新的问题
阮一峰的网络日志
阮一峰的网络日志
雷峰网
雷峰网
Microsoft Security Blog
Microsoft Security Blog
爱范儿
爱范儿
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园_首页
Apple Machine Learning Research
Apple Machine Learning Research
罗磊的独立博客
GbyAI
GbyAI
Stack Overflow Blog
Stack Overflow Blog
Martin Fowler
Martin Fowler
宝玉的分享
宝玉的分享
L
LangChain Blog
Engineering at Meta
Engineering at Meta
量子位
有赞技术团队
有赞技术团队
博客园 - 【当耐特】
A
About on SuperTechFans
Y
Y Combinator Blog

博客园 - 若-飞

电商订单为什么要存「交易快照」 Git Submodule Sync 完整技术文档 企业AI Agent落地的核心逻辑与路径 基于langchain,Function Call的成功率怎么解决? LangChain Checkpoint(检查点)是什么?—— Agent 的"存档机制" RAG 设计:Embedding 如何切分 AI 客服系统设计:RAG 知识库设计 Go 百万连接服务器设计:从网卡到业务的全链路解析 深度解析 sync.Pool:从设计哲学到生产实践 Goroutine 泄漏:原因、检测与防范 Go Channel 关闭与超时机制完全指南 Go Map 无限增长问题解决方案 LangChain 聊天记录压缩:原理、机制与实战 揭开 sklearn 文本分类的核心原理:从词袋到逻辑回归 大模型“胡说八道”怎么办?一张图读懂检测、评估与修复全方案 企业级AI知识库权限隔离设计:让AI“懂规矩”比“懂知识”更重要 RAG系统设计全解析:从架构到多模态的核心知识图谱 RAG召回率提升全攻略:7大核心方法让检索更精准 RAG召回率提升秘籍:Metadata过滤的底层原理与实践 构建更好的RAG系统:深入理解混合搜索 一文搞懂 RAG 中 Retriever 和 Reranker 的区别 一文搞懂 RAG 的召回率(Recall)是什么? LangChain / LangGraph、MCP、Harness Engineer 与 Claude Code 的对应关系 Agent Harness 技术笔记:从 Trajectory 到 Function Calling Loop BLEU 是什么?——从原理到工程实践 一文讲清:Approve / Permit / Permit2 的本质区别 分库分表后跨分页查询的完整方案 ai如何处理私有数据 ai幻觉是啥,以及如何解决 别再让大模型“凭空瞎猜”了!带你认识AI最强外挂:ChromaDB
别再乱用 LEFT JOIN 了:EXISTS 的正确理解与实战对比
若-飞 · 2026-09-20 · via 博客园 - 若-飞

一、EXISTS 到底在做什么?

一句话概括:EXISTS 只判断“有没有匹配的行”,不关心匹配了几行,也不关心匹配行里有什么。

它的逻辑语义是:对左表的每一行,拿关联字段去子查询里找,只要能找到至少一条满足条件的记录,这行就保留;一条都找不到,就丢弃。

这决定了它两个关键特性:

  • 天然去重:右表匹配多条,也只算“有”,不会让左表行数膨胀。

  • 找到即停:匹配到第一条就停止扫描,不关心右表还有多少条。

二、示例场景

两张表:

订单表 orders

退款表 xq_order_refund

注意:A001 有 2 条退款记录(一条待处理、一条已处理),A002 只有已处理,A003 有待处理。

三、查“有待处理退款的订单”

写法一:EXISTS

SELECT o.order_no, o.amount
FROM orders o
WHERE EXISTS (
    SELECT 1
    FROM xq_order_refund r
    WHERE r.order_no = o.order_no
      AND r.tenant_id = o.tenant_id
      AND r.status = 0
);

结果:

A001 只出现 1 次,因为 EXISTS 只判断“有没有”,不关心有几条。

写法二:LEFT JOIN

SELECT DISTINCT o.order_no, o.amount
FROM orders o
LEFT JOIN xq_order_refund r
    ON r.order_no = o.order_no
   AND r.tenant_id = o.tenant_id
   AND r.status = 0
WHERE r.order_no IS NOT NULL;

结果和 EXISTS 一样,但必须加 DISTINCT。如果不加,A001 会因为退款表有 2 条记录而出现 2 次(一条匹配 status=0,一条匹配为 NULL)。

四、核心差异对比

五、SELECT 1 怎么理解?

EXISTS (SELECT 1 FROM ... WHERE ...)

SELECT 1 只是一个占位符。EXISTS 不关心 SELECT 后面写什么,写 1*id 都完全等价,性能没有任何区别。

写成 SELECT 1 是约定俗成的习惯,目的是告诉读代码的人:我不需要任何列,只判断存在性。

六、效率关键:索引

EXISTS 快不快,第一决定因素是索引,不是写法本身。

理想情况:在 xq_order_refund 上建联合索引

CREATE INDEX idx_refund_order_tenant_status
ON xq_order_refund (order_no, tenant_id, status);

对每行订单做一次索引精确查找,找到即停,复杂度接近 O(N × logM),效率很高。

糟糕情况:没有索引,每行订单都全表扫描退款表,复杂度退化成 O(N × M),数据量大时会非常慢。

七、什么时候该用 EXISTS?

八、总结

  • EXISTS 的核心是存在性判断,天然去重、找到即停。

  • 它和 LEFT JOIN 不等价:LEFT JOIN 会膨胀行数,EXISTS 不会。

  • 只判断“有没有”时,EXISTS 比 LEFT JOIN 更自然、通常也更高效。

  • 索引是前提:没有索引,换任何写法都救不了。

  • 写 EXISTS 时,子查询的 WHERE 条件必须和外表关联,否则结果会全错。