☰
可回收且低脂产品筛选:SQL查询中的NULL与索引陷阱
2026/10/20 5:53:54 网站建设 项目流程

我在整理数据岗的入门题单时,又看到这道“1757. 可回收且低脂的产品”。说实话,它看起来短到不用思考:一张产品表,两个状态字段,筛选出同时满足条件的 product_id。但就是这种“三行写完”的题目,我见过不少人在面试里翻车,也在实际生产环境里踩过它的延伸坑。今天不只要讲怎么写,更要把题目背后的数据字典设计、NULL 处理、索引选择和真实业务场景一次性讲透。无论你是刚学 SQL 的新人,还是准备数据岗面试的候选人,这篇都值得花十分钟读完。

1. 为什么一道“最基础”的筛选题值得单独拆开讲

1.1 题面还原:它到底在问什么

这道题的表结构非常干净,核心就是一张产品信息表,包含三个字段:

  • product_id:产品唯一编号,主键
  • low_fats:是否为低脂产品,枚举值 'Y' 表示是,'N' 表示否
  • recyclable:是否可回收,枚举值 'Y' 表示是,'N' 表示否

要求是从这张表里找出所有“低脂且可回收”的产品编号,返回 product_id 即可。就这么简单。拆解下来,它考察的是关系型数据库里最基础的两个操作:行过滤(WHERE 条件筛选)和列投影(只 SELECT 需要的字段)。

如果只把它当一道刷题任务,那确实两三分钟结束。但凡是处理过真实业务数据的人都知道,简单题目背后往往藏着三个关键点:字段状态怎么定义、空值怎么算、条件组合是交集还是并集。这三个点,恰恰是很多人在面试中说不清楚的地方。

1.2 表结构里的隐藏信息:枚举字段没有你以为的那么“干净”

只看题面,low_fats 和 recyclable 只有 Y/N 两个取值,干净得像教科书。但真实生产环境里,这种状态字段往往是脏数据重灾区。

我自己就接过一个数据清洗需求,上游导入的生产商产品表里,low_fats 字段同一列里同时存在 'Y'、'y'、'yes'、'1'、空字符串、NULL 六种形态。原因很简单:不同业务系统导出习惯不同,有的用布尔值转字符串,有的直接手填,有的因为流程没走完就没写入。这种情况下,你用 low_fats = 'Y' 去过滤,结果必然漏数据。

所以面对这类题目,第一反应不是写 SQL,而是先确认三件事:字段是否有非空约束、枚举值是否统一大小写、业务上空值代表“不符合”还是“未录入”。这几个问题搞清楚了,SQL 怎么写其实就水到渠成。

1.3 为什么面试官偏爱这种题目

这道题的经典程度,几乎每个数据岗位的题库里都能看到,原因很简单:它能在最短时间内考察一个候选人的基本功。

新手会直接写 WHERE low_fats = 'Y' AND recyclable = 'Y',这是标准答案;但稍微有点经验的候选人会主动问一句“字段有没有 NULL”“返回结果要不要去重排序”。就是这个“多问一句”的差距,往往决定了面试官对你数据敏感度的判断。后面我会具体展开 NULL 和写法差异的问题,这里先记住一个结论:这道题不是用来背答案的,是用来暴露思维习惯的。

2. 标准写法之外:NULL 语义和条件组合才是真正的分水岭

2.1 大多数人熟悉的第一版写法

如果确认两个字段都不存在空值,写法非常简单:

SELECT product_id FROM products WHERE low_fats = 'Y' AND recyclable = 'Y';

这版写法在题面假设下完全正确,AND 表示交集,返回的是同时满足“低脂”和“可回收”两个条件的产品,正好对应“可回收且低脂”的语义。我见过不少人在这里犯迷糊,把 AND 写成 OR,结果返回了所有“低脂”或“可回收”产品的并集,产品数量直接翻倍。这个错法非常典型,用集合论的话说:AND 取交集,OR 取并集,一字之差,结果完全不同。

2.2 生产环境里躲不开的 NULL 问题

现在我们把场景换成真实业务表。产品表里某个字段因为上游没同步,low_fats 是 NULL,这行产品算不算“低脂产品”?

严格来说,SQL 三值逻辑里,NULL 代表“未知”,它既不等于 'Y',也不等于 'N'。所以 WHERE low_fats = 'Y' 的条件对 NULL 行返回的是 UNKNOWN,最终不会被选中。这意味着:如果业务上把 NULL 理解为“未录入,但本质是低脂”,直接等值过滤就会漏掉这些产品。

取决于业务口径,通常有两种处理方式:

  • 口径一:空值视为不符合条件。那不需要额外处理,WHERE low_fats = 'Y' 已经自动过滤掉 NULL。
  • 口径二:空值视为“信息缺失,但可以纳入候选”。那需要显式兜底,比如 COALESCE(low_fats, 'Y') = 'Y',把 NULL 转成 'Y' 再比较。

我经历过一个真实案例:某零售集团做环保包装筛选,要求把“可回收且低脂”的商品清单推给供应链小组。产品表里 recyclable 字段大约有 8% 的 NULL,因为部分老商品没有录入回收属性。当时业务负责人咬定“没写就是不能回收”,我们按口径一处理,筛出来的清单少了 200 多个 SKU;后来采购反馈这些 SKU 其实有部分使用可回收包装,最终改成了“NULL 也纳入,线下统一复核”的口径,才把清单补齐。你看,同一个 SQL,口径不同,结果差一大截。

2.3 三种稳定写法对比:没有绝对最优,只有场景适配

为了在不同业务口径下都能写对,我整理出三种常见写法:

-- 写法 A:默认空值不符合条件 SELECT product_id FROM products WHERE low_fats = 'Y' AND recyclable = 'Y'; -- 写法 B:空值视为符合条件(口径二) SELECT product_id FROM products WHERE COALESCE(low_fats, 'Y') = 'Y' AND COALESCE(recyclable, 'Y') = 'Y'; -- 写法 C:显式排除 NULL,语义更清晰 SELECT product_id FROM products WHERE low_fats IS NOT NULL AND low_fats = 'Y' AND recyclable IS NOT NULL AND recyclable = 'Y';

写法 A 最简洁,适合题面这种约束明确、无 NULL 的场景;写法 B 适合“缺失即默认通过”的口径;写法 C 适合希望把过滤逻辑每一步都写清楚、方便后人维护的场景。我的习惯是:线上代码宁可啰嗦一点,也要把空值处理显式写出来,这样后来接手的人不会靠猜。

注意:IN ('Y') 与 = 'Y' 在 NULL 处理上行为完全一致,都不会匹配 NULL。所以如果目的是“空值也纳入”,用 IN 解决不了问题,必须用 COALESCE 或 IS NULL 配合处理。

3. 执行计划视角:低基数列上的索引到底有没有用

3.1 主键与聚簇索引带来的隐式优势

题目里的 product_id 是主键,这一点很多人扫一眼就过去了,但它在存储引擎层面藏着性能信息。

以 MySQL InnoDB 为例,表按主键聚簇存储,主键索引的叶子节点直接包含整行数据。这意味着执行 SELECT product_id FROM products 时,即使没有其他索引,也能通过聚簇索引完成全表扫描,不需要回表。这个特性对这道题的查询是个天然利好:WHERE 条件虽然过滤了两列,但最终只需返回主键本身,数据就在索引里,扫描成本可控。

如果一个表没有主键,引擎可能使用内部的隐式行 id 聚簇,情况会复杂不少。所以看到这个题,第一反应应该是:这是一张有主键的规范化表,查询路径至少不会太差。

3.2 给 where 条件列加索引前,先想想选择性

很多初学者一聊到性能,第一反应就是“给 low_fats 和 recyclable 加索引”。这种想法可以理解,但这个场景下未必有效。

原因很简单:low_fats 和 recyclable 是低基数列,只有 Y/N 两个值。索引的选择性(去重值与总行数的比值)极低,优化器算下来很可能觉得“直接全表扫描比走索引再回表更划算”。尤其是表行数不大时,加索引纯粹是浪费空间。

如果表真的很大,且这类查询非常频繁,更合理的手段不是加单列索引,而是建联合索引 (low_fats, recyclable)。但请注意,即便如此,因为两列都是低基数,联合索引也不能把扫描区间压缩得很窄。实际业务里,我会优先看分组汇总结果是否能用物化视图或缓存承接,实在不行才考虑索引调整。

3.3 用 EXPLAIN 验证你的直觉

不要靠猜,用执行计划说话。在 MySQL 里你可以这样验证:

EXPLAIN SELECT product_id FROM products WHERE low_fats = 'Y' AND recyclable = 'Y';

重点看 type 列和 rows 列。如果 type 是 ALL,说明在走全表扫描;如果走了索引,会出现 index 或 ref。对于低基数列的等值条件,出现 ALL 不代表 SQL 写得差,而是优化器基于成本模型的正常选择。真正需要警惕的是超大表上出现 ALL,且查询频繁,那时候就要从表结构设计或汇总层想办法了。

我见过不少开发同学一看到 EXPLAIN 里 type=ALL 就慌了,觉得必须加索引。其实在数据仓库分层里,这种明细层的点查本来就不是高频场景,更多时候是在数仓维度建模阶段就把标签物化好了,应用层根本不直接查原始产品表。

4. 从题面走向业务:可回收且低脂的筛选在真实场景里怎么落地

4.1 这种题目在业务中是什么样子的

“可回收且低脂”这个组合,在真实业务里通常对应的是商品标签筛选。比如生鲜电商想推一批既符合健康标准又能用环保包装的产品;品牌方想做可持续产品线盘点;供应链想做绿色包装试点,都需要从庞大的 SKU 表里捞出满足多重属性的商品清单。

这类需求听上去简单,做起来却有个隐性难点:属性字段通常散落在多个地方。有的存在商品主数据表里,有的在标签系统里维护,有的甚至要关联供应商填报的表格。所以真实场景的第一步往往不是写 SQL,而是先做字段溯源和数据字典梳理,把 low_fats、recyclable 的准确来源和更新频率确认清楚。

4.2 组合条件筛选时最容易踩的三个坑

第一,条件组合写错。前面说过 AND 和 OR 的差异,这里再补充一个坑:如果需求是“可回收或低脂”这种并集语义,直接写 OR 会遇到 NULL 问题,因为 NULL OR 真值的结果可能不是预期。稳妥做法是用括号明确分组,并配合 COALESCE 处理。

第二,枚举值不统一。同一列里混着 'Y'/'y'/'1'/TRUE/是/空值,这类脏数据在真实环境里太常见了。我的做法是先跑一遍聚合统计,看清楚实际取值分布,再决定清洗映射逻辑:

SELECT low_fats, recyclable, COUNT(*) AS cnt FROM products GROUP BY low_fats, recyclable;

这一步能让你在建查询前就对数据质量有数,避免写完 SQL 才发现结果差了几百行。

第三,忽略排序与幂等性。题面没要求 ORDER BY,但报表输出通常需要稳定顺序。如果不排,每次查询结果顺序可能不同,下游对账就麻烦。另外,如果查询会重复执行,最好确认结果是可重复的,必要时在表名后加分区条件或时间条件,避免数据范围漂移。

4.3 一个虚构的零售场景实操

我模拟了一个典型的零售筛选需求,帮你把前面的知识点串起来。

某公司商品主数据表 product_master 里有 20 万 SKU,其中 low_fats 和 recyclable 字段均存在脏数据和 NULL。业务方要求输出一份“可回收且低脂”的商品清单,包含 product_id 和商品名称,用于环保包装试点。

我第一步先做数据探查,发现 low_fats 有 4 种取值('Y', 'N', NULL, 'y'),recyclable 有 3 种取值('Y', 'N', NULL),并且有少量行的 product_id 重复。然后我按业务口径(NULL 视为不符合,但大小写不敏感)写清洗查询:

SELECT product_id, product_name FROM product_master WHERE LOWER(low_fats) = 'y' AND LOWER(recyclable) = 'y' AND product_id IS NOT NULL ORDER BY product_id;

这里有三个设计点:LOWER 统一大小写、IS NOT NULL 排除脏数据、ORDER BY 保证输出稳定。product_id 如果存在重复,还需要根据业务规则决定是否 DISTINCT 或按更新时间取最新。这些都是题目里不会写、但实际一定会遇到的东西。

4.4 从这道题延伸出去的学习路径

“可回收且低脂”是一类题目的代表,同类型还包括“某个状态为真且另一个条件为假”“两个标签同时命中的用户”“满足任意条件的订单”等等。掌握这道题后,我建议你按这个顺序继续深入:

  • 先练熟 AND/OR/NOT 组合与 NULL 三值逻辑
  • 再学习 CASE WHEN 把布尔状态转成业务标签
  • 接着尝试 GROUP BY 做条件计数,比如统计每种产品类型的低脂可回收数量
  • 最后过渡到窗口函数,比如计算每个分类下可回收产品的占比排名

这条路径对数据岗面试的帮助很大。因为它从最简单的行过滤开始,一步步把读者引向聚合、分组、标记和窗口计算,正好覆盖日常数据需求的大部分场景。

5. 最后说点实战体会

我最早做这类题时也吃过亏。当时模拟环境的数据非常规整,我用最简单的等值查询一次通过,根本没想过 NULL 的事。后来到了真实项目里,同样的逻辑少了数据,排查了半天才发现是有几行 recyclable 是空字符串,导致结果和业务对不上。从那儿以后我养成了一个习惯:拿到任何状态字段,先探查取值分布,再确认业务口径,最后才写查询。

这套流程听起来比直接写 SQL 多花几分钟,但它能帮你避开数据质量的大坑。对于“可回收且低脂”这类产品筛选需求,核心永远不只是那三行 SQL,而是你对数据字典的理解、对空值语义的判断、以及对输出稳定性的把控。把这些想清楚,不管题目怎么变,你都能写出让业务方放心、让后来人省心的查询。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询