real
在 PostgreSQL 中对一个操作数和一个操作数执行数学运算numeric
会得到一个double precision
值,但我觉得这很令人惊讶。为什么结果不是real
, 呢?
我使用 PostgreSQL 13 和 16 确认了这种行为。
SELECT
pg_typeof(0::numeric + 0::numeric), -- numeric
pg_typeof(0::real + 0::real), -- real
pg_typeof(0::numeric + 0::real), -- double precision??
pg_typeof(0::real + 0::numeric); -- double precision??
我已阅读了 PostgreSQL 文档的大部分内容,其中最相关的是:10.2。类型转换:运算符
我已经查询过pg_catalog.pg_cast
(或psql
)\dC
来了解相关的隐式转换是什么:
> SET search_path = 'pg_catalog';
> SELECT format_type(castsource, NULL) AS source,
format_type(casttarget, NULL) AS target
FROM pg_cast
WHERE castcontext = 'i' -- i.e. casts allowed implicitly
AND ARRAY[format_type(castsource, NULL), format_type(casttarget, NULL)]
<@ '{numeric,real,double precision}'::text[];
结果是:
source | target
---------+------------------
real | double precision
numeric | real
numeric | double precision
numeric | numeric
(4 rows)
据我所知,对于0::numeric + 0::real
,PostgreSQL 应该(摘录全部来自10.2. 类型转换:运算符):
- 从系统目录中选择要考虑的运算符
pg_operator
。如果使用了非模式限定的运算符名称(通常情况),则考虑的运算符是那些在当前搜索路径中可见的具有匹配名称和参数计数的运算符。
我希望它以所有运算符开始+
- 每个运算符接受一对相同的数字类型(real + real
,,integer + integer
等等)
- 检查运算符是否准确接受输入参数类型。
real
在步骤 2 中,没有运算符与和完全匹配numeric
,因此我预计这里不会发生任何事情。
3a. 丢弃输入类型不匹配且无法转换(使用隐式转换)以匹配的候选运算符。
我预计这应该会丢弃大多数候选者。它应该只保留两个:一个是real
,另一个是double precision
它显然最终使用的 。(请注意,real
不能隐式转换为numeric
,因此它不应该保留numeric
。)
3b. 如果任何输入参数属于域类型,则在所有后续步骤中将其视为域的基类型。
我认为这不会产生任何作用,因为这里没有域类型。
3c. 遍历所有候选,并保留那些与输入类型最精确匹配的候选。如果没有精确匹配的候选,则保留所有候选。如果只剩下一个候选,则使用它;否则继续下一步。
在这里,我期望它应该丢弃 的运算符double precision
,因为 的运算符real
有一个精确匹配,而 的运算符double precision
没有精确匹配。然后,由于这里只剩下一个候选,它应该使用 的运算符real
,我期望结果是real
。但结果却是一个double precision
值。
因此,我的问题是,为什么 PostgreSQLdouble precision
在这种情况下选择运算符,它需要隐式转换两个操作数而不是其中一个操作数?文档有误,还是我的理解有误?