订单查询丢数据,查了半天原来是 WHERE 惹的祸
前两天帮着看一个从 MySQL 迁过来的老需求业务原话是这么说的“把所有订单都列出来如果这笔订单有审核中的售后申请把售后原因也带出来”。这种需求太常见了我第一反应就是LEFT JOIN——左表订单表保底右表售后表能匹配上就带出来匹配不上就是 NULL教科书式写法。结果这条 SQL 跑出来的结果让我在屏幕前愣了好几秒。这篇就把这次踩坑的过程整个记下来怎么发现的、怎么排查的、最后怎么定位到根子上、KingbaseESKES这边要怎么改。环境是KES V009R001C010连接用普通业务账号app_user不是管理员ksql-h127.0.0.1-p54321-Uapp_user-dapp_db场景还原订单表 售后表先把场景搭起来。两张表订单表t_pit_order售后服务单表t_pit_service一笔订单可能有 0 条、1 条甚至多条售后记录createtableapp_schema.t_pit_order(order_idintegerprimarykey,order_novarchar(40)notnullunique,user_idintegernotnull,order_statusvarchar(20)notnull,amountnumeric(10,2)notnull,created_attimestampnotnull);createtableapp_schema.t_pit_service(service_idintegerprimarykey,order_novarchar(40)notnull,service_statusvarchar(20)notnull,reasonvarchar(100),created_attimestampnotnull);插入 5 笔订单全部是已支付状态insertintoapp_schema.t_pit_order(order_id,order_no,user_id,order_status,amount,created_at)values(1,PIT20260701001,1001,paid,199.00,2026-06-28 09:00:00),(2,PIT20260701002,1002,paid,88.00,2026-06-28 10:00:00),(3,PIT20260701003,1003,paid,256.00,2026-06-28 11:00:00),(4,PIT20260701004,1004,paid,320.00,2026-06-28 12:00:00),(5,PIT20260701005,1005,paid,150.00,2026-06-28 13:00:00);售后记录只给两笔订单配了订单 1 是审核中订单 3 是已完成。订单 2、4、5 压根没提交过售后insertintoapp_schema.t_pit_service(service_id,order_no,service_status,reason,created_at)values(1,PIT20260701001,审核中,尺码不合适,2026-06-29 09:00:00),(2,PIT20260701003,已完成,质量问题,2026-06-29 10:00:00);这个数据设计不是随便凑的特意留了三种情况有售后且是审核中的订单1、有售后但不是审核中的订单3、压根没售后的订单2、4、5。等下就知道为什么这三种情况都得覆盖到。第一版 SQL看起来完全没毛病按业务需求写的第一版就是标准LEFT JOIN加个过滤条件selecto.order_no,o.order_status,o.amount,s.service_status,s.reasonfromapp_schema.t_pit_order oleftjoinapp_schema.t_pit_service sons.order_noo.order_nowheres.service_status审核中;我当时的预期是5 笔订单一条不少只有订单 1 的service_status、reason两列有值其余 4 行应该是 NULL。左连接嘛左表保底这是常识。实际跑出来是这样只有 1 行。订单 2、3、4、5 全部消失了包括压根没申请过售后的订单 2、4、5也包括有售后记录但状态是已完成的订单 3。说白了这条 SQL 表现得完全像一个INNER JOINLEFT这个关键字像是白写了。这时候脑子里冒出来的第一个念头是是不是数据插错了第二个念头是是不是 KES 这边 LEFT JOIN 有兼容性问题。事后证明这两个念头都是错的但排查的时候没少走弯路就顺着这个思路记录下来。先排除数据问题主表到底是不是 5 条最笨也最靠谱的办法先确认主表本身没问题selectcount(*)astotal_orderfromapp_schema.t_pit_order;selecto.order_no,o.order_statusfromapp_schema.t_pit_order oleftjoinapp_schema.t_pit_service sons.order_noo.order_no;count(*)确认是 5 条去掉WHERE之后重新跑一遍连接5 笔订单一条不少地全出来了。这一步很关键它证明了两件事数据本身没问题ON里的连接条件也没写错。问题百分之百出在加了WHERE s.service_status 审核中之后。到这一步KES 的 LEFT JOIN 有 bug这个猜测基本可以排除了——如果真是语法层面的问题去掉 WHERE 也应该有问题但它没有。真正诡异的地方在于只有加上那一句过滤行为才会变。定位执行计划里根本没有 LEFT JOIN翻了下之前存的一些技术资料才想起来这种加了 WHERE 之后 LEFT JOIN 变内连接的现象有个名字叫外连接消除Outer Join Elimination。先不管名字直接上EXPLAIN ANALYZE看看执行计划里到底发生了什么explainanalyzeselecto.order_no,o.order_status,o.amount,s.service_status,s.reasonfromapp_schema.t_pit_order oleftjoinapp_schema.t_pit_service sons.order_noo.order_nowheres.service_status审核中;看执行计划的第一行Hash Join (cost1.04..2.12 rows1 width49)。注意这里写的是Hash Join不是Hash Left Join。KES 的执行计划里如果一个连接真的按外连接执行通常会明确标出Left这个字样这里没有说明这条 SQL 提交上去之后优化器压根就没按外连接去跑它直接把整个查询当成了一个普通的内连接来处理。再往下看细节t_pit_service这张表被Seq Scan扫描带了个Filter: ((service_status)::text 审核中::text)后面还跟着一行Rows Removed by Filter: 1——这张表总共 2 行数据过滤之后只剩 1 行订单1那条审核中的记录订单3的已完成被过滤掉了。然后这 1 行拿去跟订单表做Hash Join订单表那边是全量 5 行参与哈希探测最终匹配上的只有 1 行。这就是问题的真相优化器把WHERE s.service_status 审核中这个条件理解成了只有t_pit_service里状态是审核中的行才有资格参与连接那些原本因为左连接才应该保留下来的、t_pit_service一侧是 NULL 的行订单2、4、5 没有售后记录订单3 的售后记录被过滤掉全都在这一步被连带着筛没了。原因很简单NULL 和 ‘审核中’ 做等值比较结果既不是真也不是假是未知在 WHERE 里未知等于被扔掉。既然这些左连接产生的 NULL 行反正都要被 WHERE 干掉那优化器算了一笔账外连接加这个过滤跟直接内连接加这个过滤结果集是完全一样的内连接开销更小于是它就悄悄把外连接换成了内连接去跑。这个换算过程不会报错、不会有任何提示SQL 文本里写的还是LEFT JOIN只是最终执行的已经不是那么回事了。这套换算规则背后其实有一套明确的判断标准比如什么样的条件一定会触发这种改写、什么样的条件不会细节挺多这里不整个铺开讲先把这次踩坑的现象和定位方法记牢——遇到LEFT JOIN 结果比预期少第一反应就是去看执行计划里连接节点到底是不是真的按外连接在跑。修复把过滤条件挪到 ON 里原因搞清楚了修复思路也就明确了真正要保留左连接语义过滤右表的条件不能写在 WHERE得写进 ONselecto.order_no,o.order_status,o.amount,s.service_status,s.reasonfromapp_schema.t_pit_order oleftjoinapp_schema.t_pit_service sons.order_noo.order_noands.service_status审核中;这次 5 行全部老老实实地回来了。只有订单 1 的service_status/reason有值订单 2、3、4、5 全是空。这里特别要盯一眼订单 3——它明明有一条售后记录“已完成”但因为不满足and s.service_status 审核中这个条件它的售后字段依然是空。这一点很能说明问题这不是运气好凑对了行数是逻辑真的对了。ON决定的是怎么连先按审核中这个条件筛一遍右表筛完了再拿去跟左表做外连接WHERE决定的是连完了再筛掉什么加在 WHERE 里就等于是连接做完之后才动手为时已晚NULL 行早被判了死刑。我自己的理解是这么记的只要是左连接右表相关的过滤条件除非目的就是顺便把左表也筛掉一批不然一律先考虑放 ON 里别图省事直接甩进 WHERE。顺手纠正一个容易搞混的需求查没有审核中售后的订单排查这条 SQL 的时候同事顺嘴问了一句“那如果我就是想查哪些订单没有审核中的售后是不是直接把等号换成!就行”——这也是个常见的误区赶紧一起验证了一下selecto.order_no,o.order_statusfromapp_schema.t_pit_order oleftjoinapp_schema.t_pit_service sons.order_noo.order_noands.service_status审核中wheres.service_idisnull;结果返回订单 2、3、4、5四条。注意订单 3 也在里面——它虽然有售后记录但状态是已完成在这次以审核中为条件的关联里等于没匹配上所以service_id也是 NULL也算进了没有审核中售后这一批。这条 SQL 的关键在于IS NULL判断的是右表整体有没有匹配上而不是某个值等不等于什么用它才能真正抓住外连接产生的空行。如果真按同事说的把等号换成不等号写进 WHERE会重新掉进最开始那个坑里这里就不重复验证了逻辑和前面一样。这次踩坑的收获整理成几条能直接抄进 Code Review 清单的东西看到LEFT/RIGHT JOIN后面又跟着WHERE 右表字段 xxx先停一下。这种写法十有八九是把过滤条件放错了地方先问自己一句这个条件是要筛掉左表一部分数据还是单纯想看右表某个值前者才适合放 WHERE。右表的过滤条件只要目的是保留外连接语义一律下推到 ON。这条不是看情况是默认动作。怀疑外连接有问题直接跑EXPLAIN ANALYZE别猜。看执行计划里连接节点是不是明确带着Left字样不带的话十有八九是被优化器改写了。查缺失用IS NULL别用不等号。不等号一样会被 WHERE 的 NULL 语义坑一遍IS NULL才是唯一可靠的判断没匹配上的方法。这个问题从 MySQL/PostgreSQL 迁移过来的人尤其容易中招因为写法上跟以前一模一样SQL 一个字没改行为却因为优化器的这个动作变了代码审查根本看不出来只能靠跑一遍执行计划确认。这条坑最恶心的地方就在这儿语法完全合法SQL 正常执行不报任何错误只是安安静静地把你以为该在的数据丢掉了。真要命的问题从来不是报错的那种是这种看起来什么都没发生的。

相关新闻

NocoBase 2.0 无代码搭建工单系统:1小时完成从CRM集成到自动化流程配置

NocoBase 2.0 无代码搭建工单系统:1小时完成从CRM集成到自动化流程配置

NocoBase 2.0 无代码搭建工单系统:1小时完成从CRM集成到自动化流程配置1. 为什么选择无代码工单系统?在快节奏的商业环境中,效率就是竞争力。传统工单系统开发往往面临三大痛点:开发周期长(通常需要2-3个月&#xff09…

2026/7/10 12:03:39 阅读更多 →
【限时解密】Gemini Enterprise Tier认证通道关闭倒计时:Google Partner团队透露的最后48小时准入策略

【限时解密】Gemini Enterprise Tier认证通道关闭倒计时:Google Partner团队透露的最后48小时准入策略

更多请点击: https://intelliparadigm.com 第一章:Gemini Enterprise Tier认证通道关闭倒计时全局洞察 Google 已正式宣布 Gemini Enterprise Tier 的专属认证通道将于 2024 年 12 月 15 日永久关闭。该通道曾面向企业客户开放,用于申请定制…

2026/7/10 12:01:38 阅读更多 →
STM32F469II与ADS131M02构建高精度数据采集系统

STM32F469II与ADS131M02构建高精度数据采集系统

1. 项目背景与核心需求解析 在工业测量、医疗设备和精密仪器等领域,高精度模数转换(ADC)是模拟信号数字化的关键技术。传统方案往往面临分辨率不足、噪声干扰严重或功耗过高等问题。ADS131M02作为TI推出的24位ΔΣ ADC,配合STM32F…

2026/7/10 12:01:38 阅读更多 →

最新新闻

Kodi字幕插件完全指南:如何告别手动搜索,实现智能字幕匹配?

Kodi字幕插件完全指南:如何告别手动搜索,实现智能字幕匹配?

Kodi字幕插件完全指南:如何告别手动搜索,实现智能字幕匹配? 【免费下载链接】zimuku_for_kodi Kodi 插件,用于从「字幕库」网站下载字幕 项目地址: https://gitcode.com/gh_mirrors/zi/zimuku_for_kodi 还在为找不到合适的…

2026/7/10 13:03:48 阅读更多 →
离线安装vscode server以及免密登录

离线安装vscode server以及免密登录

离线安装vscode server以及免密登录 参考:VSCode Remote 报错,无法连接?? 背景 代码服务器通常是无网状态,并且普通用户的权限很低,无法在服务器中安装vscode软件,而我们又想要用vscode编辑器&…

2026/7/10 13:03:48 阅读更多 →
Video2X终极指南:如何用AI免费将模糊视频变4K清晰

Video2X终极指南:如何用AI免费将模糊视频变4K清晰

Video2X终极指南:如何用AI免费将模糊视频变4K清晰 【免费下载链接】video2x A machine learning-based video super resolution and frame interpolation framework. Est. Hack the Valley II, 2018. 项目地址: https://gitcode.com/GitHub_Trending/vi/video2x …

2026/7/10 13:03:48 阅读更多 →
JSON解析

JSON解析

JSON简介 JAVAScript Object Notation是一种轻量级的数据交换格式 具有良好的可读和便于快速编写的特性。 业内主流技术为其提供了完整的解决方案(有点类似于正则表达式 ,获得了当今大部分语言的支持) JSON采用兼容性很高的文本格式&#xff…

2026/7/10 12:59:26 阅读更多 →
数学学习指南

数学学习指南

基础数学学习地图 北京大学数学与应用数学双学位的学分要求 参考文献 Mathematics | MIT OpenCourseWare | Free Online Course Materials我想业余时间系统学习数学,想求一份学习地图,请问我应该该怎么学习呢?学习哪些 和教程呢?…

2026/7/10 12:59:26 阅读更多 →
前馈控制与反馈控制对比

前馈控制与反馈控制对比

自动控制系统中主要包含两大类控制,一种是前馈控制,另一种是反馈控制。那么二者之间究竟有怎么的区别和联系。本文做简要介绍。 1. 前馈控制属于开环控制,反馈控制属于负反馈的闭环控制 在自动控制系统中,反馈控制是闭环控制&am…

2026/7/10 12:59:26 阅读更多 →

日新闻

STM32与LTC1864高精度ADC的SPI通信实现

STM32与LTC1864高精度ADC的SPI通信实现

1. 项目背景与核心需求在工业控制和嵌入式系统开发中,模拟信号与数字系统的无缝集成一直是工程师面临的关键挑战。LTC1864作为一款16位高精度ADC转换器,配合STM32F101ZG这类主流微控制器,能够构建高性能的模拟信号采集系统。这种组合特别适合…

2026/7/10 0:03:07 阅读更多 →
猫抓插件:浏览器资源嗅探与视频下载的终极解决方案

猫抓插件:浏览器资源嗅探与视频下载的终极解决方案

猫抓插件:浏览器资源嗅探与视频下载的终极解决方案 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 还在为网页视频无法下载而烦恼吗&am…

2026/7/10 0:05:09 阅读更多 →
直流有刷电机驱动方案:TC78H653FTG与MKV46F256VLH16应用

直流有刷电机驱动方案:TC78H653FTG与MKV46F256VLH16应用

1. 直流有刷电机驱动方案概述在工业自动化和消费电子领域,直流有刷电机因其结构简单、控制方便、成本低廉等优势,仍然是许多应用场景的首选驱动方案。TC78H653FTG作为东芝推出的新一代H桥驱动器,与MKV46F256VLH16微控制器配合使用&#xff0c…

2026/7/10 0:05:09 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/8 16:14:06 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/9 13:46:46 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/9 21:41:05 阅读更多 →

月新闻