SQLite 列亲和性:文本 8.00 与数值 8,为什么可能匹配不上
看起来一样的数,可能按文本比较
导入表把测量读数保存为 TEXT,页面传入数字八查询,表里明明有 8.00 却找不到。参数绑定本身没有失效;SQLite 会结合列的类型亲和性决定比较前的转换,TEXT 列面对数值参数时,可能先把参数转换成文本再比较。
这里要区分存储类和列亲和性。typeof 可以观察某个值当前是 text、integer 还是 real;列亲和性描述的是列在存储和比较时偏好的转换规则。二者相关,但不能只看建表时写了什么名字,就推断每一行保存的实际类型。
保持 SQL 不变,只更换参数类型
下面用 Python 3.12 自带 sqlite3 驱动,已在 SQLite 3.53.1 验证。所有操作都在独立内存库里,查询用占位符绑定参数。四条读数数值相同但文本形式不同,再加一条无效文本,方便对照数值转换是否也承担了输入校验。
AI概念示意图,非真实界面
import sqlite3
connection = sqlite3.connect(':memory:')
try:
connection.execute('CREATE TABLE readings(value TEXT)')
connection.executemany(
'INSERT INTO readings VALUES (?)',
[(v,) for v in ('8', '8.0', '8.00', '08.00', 'invalid')],
)
expected = [['8'], ['8.0'], ['8.00']]
for parameter, wanted in zip((8, 8.0, '8.00'), expected):
rows = connection.execute(
'SELECT value FROM readings WHERE value = ? ORDER BY rowid',
(parameter,),
).fetchall()
values = [row[0] for row in rows]
print(type(parameter).__name__ + ':', values)
assert values == wanted
numeric = connection.execute(
'SELECT value FROM readings '
'WHERE CAST(value AS NUMERIC) = ? ORDER BY rowid', (8,)
).fetchall()
print('numeric comparison:', [row[0] for row in numeric])
assert numeric == [('8',), ('8.0',), ('8.00',), ('08.00',)]
invalid = connection.execute(
"SELECT CAST('invalid' AS NUMERIC)"
).fetchone()[0]
print('invalid cast:', invalid)
assert invalid == 0
connection.execute('CREATE TABLE typed(value NUMERIC)')
connection.executemany(
'INSERT INTO typed VALUES (?)', [('8.00',), ('invalid',)]
)
stored = connection.execute(
'SELECT value, typeof(value) FROM typed ORDER BY rowid'
).fetchall()
print('numeric affinity stores:', stored)
assert stored == [(8, 'integer'), ('invalid', 'text')]
finally:
connection.close()参数绑定保留的是值和类型
前三次查询依次输出 int: ['8']、float: ['8.0']、str: ['8.00']。整数八、浮点八和字符串 8.00 经过 TEXT 列的比较规则后,产生了不同文本匹配结果。参数化查询负责正确传递参数,并不强制所有比较变成数值语义。
第四次显式把列转换成 NUMERIC,结果包括四种数字文本。这个查询主动选择了数值相等的口径,因此前导零和末尾小数零不再区分。若列保存的是产品代码或外部编号,保留这些差异可能恰好是业务要求,不能随意转换。
本次实验使用能精确表示的小整数,不涉及小数测量误差。真实数据若含小数、科学计数法或很大的整数,还应单独定义精度和范围。把某次转换后碰巧相等当成适用于全部数字文本的规则,会掩盖后续数据问题。
显式转换不是合法性证明
示例还打印 invalid cast: 0,说明无效数字文本经 CAST AS NUMERIC 后可能变成零。若随后查询等于零的记录,无效值就有机会混入结果。因此,不应拿“转换没有抛错”作为输入合法的标准,应在进入表前明确检查格式与范围。
最后一张 NUMERIC 列的表保存了文本 8.00 和 invalid。读取结果分别是整数八与原来的无效文本,typeof 对应 integer 和 text。这说明普通 SQLite 表的亲和性会尽力转换合适的内容,但并不会把 NUMERIC 声明变成严格的数字输入校验。
对新表,先决定这一列代表可计算量还是需要保留格式的标识。如果两种用途都存在,可以分别保存已校验的标准数值与原始输入。这样筛选和展示使用各自明确的字段,不必在每条查询中猜测文本原来想表达什么。
对已有表,不要直接批量覆盖原始值。先抽样查看 typeof、异常文本和转换前后差异,再制定清洗规则。显式 CAST 适合调查或经过评估的查询,但给列套表达式也可能影响普通索引的使用,应同时检查实际查询计划。
验收时把 SQL、列定义和参数类型一起记录。同一条查询在命令行里用数字字面量、在应用里绑定字符串,可能不是同一组比较条件。把调用侧类型也纳入复现材料,才能解释为什么人工查询成功而应用仍然查不到记录。


