今天,我在SQL中遇到了<操作员的一种极其违反直觉的行为,我不知道为什么.

<插入完全相同的操作数会根据上下文给出不同的结果.

有人能解释一下原因吗?这是如此违反直觉,我甚至怀疑这是SQL的一个错误.

SQL

DROP TABLE IF EXISTS t1;

CREATE TABLE
    t1 (s VARCHAR(10));

INSERT INTO
    t1 (s)
VALUES
    ('0001/a'),
    ('0001/b'),
    ('0002/a'),
    ('0002/b'),
    ('0003/a'),
    ('0003/b');

SELECT ('/' < ':'); -- => `TRUE`

SELECT ('0002/a' < '0002:'); -- => `TRUE`

SELECT ('0002/b' < '0002:'); -- => `TRUE`

SELECT * FROM t1 WHERE (s < '0002:'); -- => What will the result be?

预期结果

+--------+
| s      |
+--------+
| 0001/a |
| 0001/b |
| 0002/a |
| 0002/b |
+--------+

实际结果

+--------+
| s      |
+--------+
| 0001/a |
| 0001/b |
+--------+

环境

我使用的是没有特殊配置的容器版本:

$ docker run -d -p 3306:3306 --name mysql \
  -e 'MYSQL_DATABASE=t' \
  -e 'MYSQL_ROOT_PASSWORD=password' \
  --restart always mysql

编辑 1

我可以用DB Fiddle来重现这种行为:

编辑 2

 > show variables like "%collat%"
+-------------------------------+--------------------+
| Variable_name                 | Value              |
+-------------------------------+--------------------+
| collation_connection          | utf8mb3_general_ci |
| collation_database            | utf8mb4_0900_ai_ci |
| collation_server              | utf8mb4_0900_ai_ci |
| default_collation_for_utf8mb4 | utf8mb4_0900_ai_ci |
+-------------------------------+--------------------+

推荐答案

正如 comments 中指出的那样,不同的排序将导致字符的顺序不同,因此如果您的比较具有两种不同的排序,则可能会得到两种不同的结果.

在您的情况下,数据库默认排序规则和连接默认排序规则是不同的,因此当您将字符串字面量与字符串字面量进行比较时,将使用连接默认值,但当您将列与字符串字面量进行比较时,将使用列排序规则,如果在创建表时没有显式设置,则将是数据库默认值.所以你会得到两种不同的结果.

这在collation_connection的文档中引用(强调我的)

连接字符集的排序规则.collation_connection is important for comparisons of literal strings.比较 具有列值的字符串、collision_connect并不重要 因为列有自己的排序,排序更高 优先级(请参阅Section 12.8.4, “Collation Coercibility in Expressions").

Mysql相关问答推荐

比较2个mysql json数据类型列

亚马逊RDS MySQL-无法';t执行';使用读取锁定刷新表';

Hibernate 不创建表'

"Shelf服务器容器访问MySQL Docker容器时出现SocketException异常"

使用 SELECT 时应锁定多少行 .. FOR UPDATE LIMIT 1

使用sql查找源和最终目的地

计算MySQL中的连续出现

从三个不同的表中生成两列,并使用分组变量计算这些列的比率

在 SQL 中 for each 组返回具有最大值的行,包括具有相同值的行

MYSQL:范围匹配与周年纪念日

MySQL - 考虑到所有前几天,每天计算唯一用户

更新和替换未加引号的 JSON 字符串

Mysql insert with loop out of select 语句

如何使用 sequelize 将复合主键放在连接表中?

仅在 MYSQL DATEDIFF 中显示小时数

mysql 数据类型仅存储月份和年份

应该使用什么列类型将序列化数据存储在 mysql 数据库中?

MySQL在哪里存储数据库文件?

如何将本地托管的 MySQL 数据库与 docker 容器连接

获得上个月的第一天和最后一天的最佳方式?