当我尝试比较包含斜杠“/”或问号“?”的字符串时,我的 Postgres 服务器给了我非常混乱的结果。例如,在 psql 中我运行:
select ('/' < '1') as c1,
('/1' < '1') as c2,
('/////1' < '1') as c3,
('/1' < '2') as c4,
('/1' < '11') as c5;
结果是:
c1 | c2 | c3 | c4 | c5
----+----+----+----+----
t | f | f | t | t
所以
'/'
小于'1'
,但'/1'
大于'1'
。事实上 '/1'
介于 '1'
和 '2'
之间,'/////1'
也是如此。这不符合字典顺序。
但是,
'/1'
(正确地)小于11
,这让我更加困惑。
我想看看
'/1'
是否被视为转义。于是我就跑了:
select length('/1');
我得到了
2
,这意味着 postgres 确实将 '/1'
视为两个字符的字符串。
当我将
/
替换为其他符号(例如 $
或 ?
)时,也会发生同样的问题。
如果您有 docker,则可以通过在 docker 容器中运行 postgres 轻松重现此问题:
docker run postgres:11
docker exec -it `docker ps | grep postgres:11 | cut -d' ' -f 1` psql -U postgres
然后尝试上面的 SQL。我尝试了 postgres 10 图像,行为是相同的。
当我将 VARCHAR 列与字符串文字进行比较时,真实的 SQL 也会发生同样的情况。这个问题让我抓狂,因为我需要编写正确的 SQL 来比较文件路径,其中显然包含许多“/”符号。
我搜索后没有找到任何讨论此问题的文档,因此这看起来不像是 postgres 的“官方功能”。按照字典顺序编写比较的正确方法是什么?
提前非常感谢。
Postgres 使用操作系统的排序规则(在 Linux 上,由
glibc
提供)。所以你的结果取决于底层操作系统。
您可以使用
"C"
排序规则强制进行 ASCCI 比较(就像我在上面的示例中所做的那样):
select '/1' > '1' collate "C"
这似乎在所有平台上都一样工作。或者,您可以指定一个 ICU 排序规则,该排序规则在所有平台上也同样有效。
您提到您想要比较文件路径。仅在“名称”上执行此操作(忽略分隔符”)的一种方法是将路径转换为数组
string_to_array(filepath, '/')
,然后使用该数组进行排序或比较。
我也遇到过同样的问题
select ('|' < 'x') as c1,
('|x' < 'x') as c2,
('|1' < 'x1') as c3,
('||' < 'x|') as c4,
('||1' < 'x|1') as c5;
c1 | c2 | c3 | c4 | c5 |
---|---|---|---|---|
真实 | 真实 | 真实 | 真实 | 真实 |
将右侧的“x”替换为“0”
select ('|' < '0') as c1,
('|x' < '0') as c2,
('|1' < '01') as c3,
('||' < '0|') as c4,
('||1' < '0|1') as c5;
c1 | c2 | c3 | c4 | c5 |
---|---|---|---|---|
真实 | 假 | 假 | 真实 | 假 |
这是在 Azure 上的 PostgreSQL 上 x86_64-pc-linux-gnu 上的 PostgreSQL 16.3,由 gcc (GCC) 11.2.0 编译,64 位
在 Windows 上,一切都很好。