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'),保证位数一致。但显然,这不适用于现在这种纯数字场景。

相关推荐
slandarer7 小时前
MATLAB | R2026b 更新了哪些有趣的新东西
开发语言·数据库·matlab
其实防守也摸鱼7 小时前
SQL注入实验笔记
android·运维·数据库·安全·oracle·自动化
做运维的阿瑞7 小时前
一张用户表串懂 MySQL 的库、表、列、行、主键
数据库·sql·mysql·oracle
细嗅蔷薇@7 小时前
MySQL中数据类型介绍
数据库·mysql
JosieBook8 小时前
【数据库】MySQL 实战精通系列 · 第3篇:SQL 核心与复杂查询实战
数据库·sql·mysql
Dragon_qu·x8 小时前
MongoDB 高可用集群部署
运维·数据库·mongodb·k8s·helm
爱签AI电子合同8 小时前
电子合同大批量怎么测?并发与批量处理维度专项测评
服务器·数据库·人工智能·企业微信·电子签名
JosieBook8 小时前
【数据库】MySQL 实战精通系列 · 第4篇:索引原理与执行计划实战
android·数据库·mysql
m0_547486668 小时前
《数据库原理、技术与应用:MySQL》全套PPT课件2026
数据库·mysql