浮点数匹配失败源于二进制近似存储导致的精度误差,子查询与外层等值比较时误差叠加,使WHERE id IN (SELECT id FROM orders WHERE amount = 19.99)常返回空结果。

嵌套查询里浮点数匹配失败,不是语法问题,而是浮点值在子查询和外层比较时已产生不一致的二进制表示——WHERE id IN (SELECT id FROM orders WHERE amount = 19.99) 很可能查不到任何结果,哪怕表里真有这笔订单。
子查询返回浮点值时,外层等值判断必然失效
浮点字段(如 FLOAT、REAL)在子查询中参与计算或筛选时,其内部存储值已是近似值。外层用 = 直接匹配,相当于拿一个近似值去比另一个近似值,误差叠加后几乎不可能相等。
- 常见错误现象:
SELECT * FROM users WHERE id IN (SELECT user_id FROM payments WHERE price = 99.99)返回空,但SELECT user_id FROM payments WHERE ABS(price - 99.99) 能查到数据 - 子查询中的
price若是FLOAT类型,即使你写WHERE price = 99.99,数据库实际匹配的是类似99.99000000000002或99.98999999999998的值 - 不同数据库对字面量
99.99的默认类型推断不同:PostgreSQL 当作NUMERIC,MySQL 可能当DOUBLE,导致子查询和外层隐式转换路径不一致
嵌套查询中必须统一转为 DECIMAL 再比较
唯一可靠的做法,是在子查询内部就把浮点字段显式转成定点数,确保参与 IN、=、JOIN 的值是精确可比的。
- PostgreSQL 写法:
SELECT * FROM users WHERE id IN (SELECT user_id FROM payments WHERE price::DECIMAL(10,2) = 99.99) - MySQL 写法:
SELECT * FROM users WHERE id IN (SELECT user_id FROM payments WHERE CONVERT(price, DECIMAL(10,2)) = 99.99) - SQL Server 写法:
SELECT * FROM users WHERE id IN (SELECT user_id FROM payments WHERE CAST(price AS DECIMAL(10,2)) = 99.99) - 别在子查询外再做转换——比如
WHERE id IN (SELECT CAST(user_id AS INT) ...)没用,关键是比较依据必须是定点数
JOIN 场景下浮点匹配更危险,必须提前固化精度
用浮点字段做 JOIN 条件时,嵌套子查询 + 浮点列组合极易导致漏关联或重复关联,尤其当两边表字段类型不一致(一边 FLOAT,一边 DECIMAL)。
- 错误写法:
SELECT u.name, p.amount FROM users u JOIN (SELECT user_id, price FROM payments) p ON u.id = p.user_id AND p.price = 19.99——p.price是子查询里的原始浮点值,不可靠 - 正确写法:把精度转换提前到子查询内,且确保 JOIN 字段类型一致:
SELECT u.name, p.amount FROM users u JOIN (SELECT user_id, price::DECIMAL(10,2) AS price FROM payments) p ON u.id = p.user_id AND p.price = 19.99 - 若子查询含聚合(如
SUM()),必须在聚合前就转DECIMAL:SUM(CAST(price AS DECIMAL(15,2))),否则SUM结果仍是浮点,后续比较白搭 - 注意:所有转换都应在子查询最内层完成,不能指望外层
CAST修复已污染的数据流
真正麻烦的不是怎么写 SQL,而是子查询里一旦用了浮点字段,整个链路就失去了确定性——哪怕你每个环节都加了 ROUND(),只要中间某步没强制转 DECIMAL,误差就已渗入结果集。别依赖“看起来一样”,只信任类型明确、精度可控的定点数表达式。

















