where id > ‘5‘ 的查询 sql 查不到 id = ‘10‘ 的数据?

一、现象

sql 复制代码
-- 注意:id 字段是 varchar 类型
highgo=# create table test(id varchar,name varchar);
CREATE TABLE
highgo=# insert into test values ('1','a'),('2','b'),('3','c'),('4','d'),('5','e'),('6','f'),('7','g'),('8','h'),('9','i'),('10','j'),('100','k'),('1000','l');
INSERT 0 12
highgo=# select * from test;
  id  | name
------+------
 1    | a
 2    | b
 3    | c
 4    | d
 5    | e
 6    | f
 7    | g
 8    | h
 9    | i
 10   | j
 100  | k
 1000 | l
(12 rows)

想查询 id 大于 '5' 的所有记录,于是执行了下面这条SQL,本以为会得到 6,7,8,9,10,100,1000,结果却只返回了 4 条:

sql 复制代码
highgo=# select * from test where id > '5';
 id | name
----+------
 6  | f
 7  | g
 8  | h
 9  | i
(4 rows)

10、100、1000 哪去了? 难道它们不比 5 大吗?更诡异的是,如果去掉引号再查,10、100、1000 又回来了!:

sql 复制代码
highgo=# select * from test where id > 5;
  id  | name
------+------
 6    | f
 7    | g
 8    | h
 9    | i
 10   | j
 100  | k
 1000 | l
(7 rows)

二、原因:ASCII码字典序 vs 数值序

1. 加了引号 >,是字符串比较

  当条件写成 WHERE id > '5' 时,数据库会把它当作字符串(文本)之间的比较 。字符串比较的规则是:从左到右,逐位比较ASCII码值(也就是我们常说的字典序)。

  • 字符 '6' 的ASCII码是 54 ,'5' 是 53 ,所以 '6' > '5' 成立。
  • 但是,'10' 的第一个字符是 '1',ASCII码是 49 。49 比 53('5')小,所以比较器直接判定 '10' < '5',根本不会去看后面的 '0'。
  • 同理,'100' 和 '1000' 的首字符也都是 '1',统统被判为小于 '5'。

  这就像我们查字典时,所有以 "A" 开头的单词,永远排在所有以 "B" 开头的单词前面 ,不管它后面有多长。甚至,如果你查 WHERE id > '9',返回结果是空(0行),因为 '10' 的首字符 '1' 小于 '9'。

2. 不加引号 > 5,是数值比较

  当条件写成 WHERE id > 5 时,数据库发现左边是 VARCHAR,右边是 INTEGER,类型不匹配。于是,数据库默默做了隐式类型转换 ,把 id 字段的内容转换成了数值类型(比如 double precision)再去比较。这时候就是实打实的数值比大小了:6 > 5、10 > 5、100 > 5、1000 > 5 全都成立,所以它们都回来了。通过执行计划可观察到是数值还是字符串比较:

sql 复制代码
highgo=# explain(analyze,buffers) select * from test where id > '5';
                                            QUERY PLAN
--------------------------------------------------------------------------------------------------
 Seq Scan on test  (cost=0.00..20.12 rows=270 width=64) (actual time=0.007..0.007 rows=4 loops=1)
   Filter: ((id)::text > '5'::text)
   Rows Removed by Filter: 8
   Buffers: shared hit=1
 Planning Time: 0.022 ms
 Execution Time: 0.020 ms
(6 rows)

highgo=# explain(analyze,buffers) select * from test where id > 5;
                                            QUERY PLAN
--------------------------------------------------------------------------------------------------
 Seq Scan on test  (cost=0.00..24.18 rows=270 width=64) (actual time=0.011..0.012 rows=7 loops=1)
   Filter: ((id)::double precision > '5'::double precision)
   Rows Removed by Filter: 5
   Buffers: shared hit=1
 Planning Time: 0.043 ms
 Execution Time: 0.021 ms
(6 rows)

三、解决方案

为了避免用户因为这种规则查不到想要的数据,我们在设计和写SQL时,有几种解法:

方案一:建表时用对数据类型(最推荐)

如果 id 本身就是一个数字编号,建表时直接使用 INTEGER 或 BIGINT ,不要用 VARCHAR。这是从根源上解决问题。

sql 复制代码
CREATE TABLE test (
    id INT,  -- 直接定义为数字类型
    name VARCHAR
);

方案二:查询时显式类型转换(适用于字段无法改类型)

如果字段已经定死了是 VARCHAR 且改不了,那就在查询时手动把字符串转成数字:

sql 复制代码
SELECT * FROM test WHERE id::INT > 5;
-- 或者
SELECT * FROM test WHERE CAST(id AS INTEGER) > 5;

注意:这种写法会走不了索引(除非建函数索引),数据量大的时候要小心性能。

方案三:如果必须按字符串存,且想按数值排序/比较

 如果字段必须存字符串(比如存的是 'A001'、'B002' 这种),那就老老实实用字典序,并提前给数字补零(如 '001'、'010'、'100'),保证位数一致。但显然,这不适用于现在这种纯数字场景。

相关推荐
这个DBA有点耶8 小时前
从症状到根因:数据库监控指标的分层方法与静态/动态阈值策略
数据库·mysql·代码规范
SelectDB9 小时前
时间旅行与快照能力速查:Doris 侧三条落地路径
大数据·数据库·数据分析
SelectDB9 小时前
云数仓成本结构拆解速查:五层计费 vs Doris / 托管形态
大数据·数据库·数据分析
SelectDB9 小时前
什么场景能承接与建议保留:Doris vs Snowflake 边界速查
大数据·数据库·数据分析
ProcessOn10 小时前
纯干货!MySql实战02:一条SQL更新语句执行流程大探索!
sql·mysql·sql server
这个DBA有点耶11 小时前
DELETE没删多少数据,表却越来越大——InnoDB删除空洞与页合并失效的根因
数据库·mysql·代码规范
ly768914 小时前
PostgreSQL 备份恢复策略:pg_basebackup、WAL 归档与 PITR 在企业 Java 系统中的落地
java·数据库·postgresql·pitr·pg_basebackup·wal 归档
小羊没烦恼!15 小时前
Ajax与JSON的一些总结
android·java·前端·数据库·mysql·算法·okhttp
注塑机数据采集联网15 小时前
如何解决注塑机数据采集联网对接MES系统数据WiFi传输数据不稳问题
大数据·数据库·物联网·数据挖掘·数据分析