比较日期字符串时的神秘不等式行为

abe*_*bop 1 postgresql collation datatypes cast

有人可以解释这种神秘的 Postgres 行为吗?我正在psql命令行执行以下查询。

mydb=> select '20150526' > '2015-05-25';
-[ RECORD 1 ]
?column? | t

mydb=> select '20150526' > '2015-05-26';
-[ RECORD 1 ]
?column? | f

mydb=> select '20150526' > '2015-05-27';
-[ RECORD 1 ]
?column? | f
Run Code Online (Sandbox Code Playgroud)

如果 Postgres 实际上是直接比较字符串,则这三个值要么为真,要么为假,这取决于字符排序的定义方式。这似乎表明 Postgres 试图在比较之前将字符串转换为日期,大概是基于对格式的一些启发。然而:

mydb=> select '20150526' < '2015-05-27';
-[ RECORD 1 ]
?column? | t

mydb=> select '20150526' < '2015-05-26';
-[ RECORD 1 ]
?column? | t

mydb=> select '20150526' < '2015-05-25';
-[ RECORD 1 ]
?column? | f
Run Code Online (Sandbox Code Playgroud)

请注意,如果将启发式解释为日期,则中间查询的结果是不正确的!相反的问题发生在>=操作员身上。这在我正在使用的触发器函数中导致了一个极其微妙的错误。

所以,有两个谜团:

  1. Postgres 似乎启发式地将这些字符串解释为日期以便进行比较。这有点令人惊讶,所以我想确认一下。
  2. 当日期相同但格式不同时,<>=运算符的结果是错误的。这似乎是一个错误。

Erw*_*ter 7

是什么让你觉得你在比较日期

实际上,您正在比较字符串文字- 在没有强制转换上下文和任何显式强制转换的情况下 - 默认为text

SELECT '20150526' > '2015-05-26' AS text2text
     , '20150526'::date > '2015-05-26'::date AS date2date;

text2text | date2date
----------+----------
 t        | f
Run Code Online (Sandbox Code Playgroud)

尝试:

SELECT pg_typeof(col) FROM (VALUES ('20150527')) t(col);  -- no cast

pg_typeof
---------
text
Run Code Online (Sandbox Code Playgroud)

text值比较的结果取决于您的语言环境,准确地说取决于COLLATION规则。要查看COLLATION会话的默认值:

SHOW lc_collate;
Run Code Online (Sandbox Code Playgroud)

COLLATION也可以按列甚至每个表达式设置。要查看COLLATION任何表达式的实际使用collation for (any)构造

SELECT collation for (my_column) FROM tbl LIMIT 1;
Run Code Online (Sandbox Code Playgroud)

要测试字符串在您的语言环境中的排序方式:

SELECT *
FROM  (
   VALUES
     ('20150526')
   , ('2015-05-25')
   , ('20150525')
   , ('2015-05-26')
   , ('20150527')
   , ('2015-05-27')
   ) t(col)
ORDER BY col;    -- COLLATE "C"
Run Code Online (Sandbox Code Playgroud)

通过附加COLLATE "C"到,将其与纯字节值的排序顺序进行比较ORDER BY。要查看与不同的比较结果COLLATION

SELECT '20150526' > '2015-05-26' COLLATE "C" AS text2text_c;
Run Code Online (Sandbox Code Playgroud)