在 PostgreSQL 中对
real
操作数和 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
等)
- 检查是否存在完全接受输入参数类型的运算符。
在步骤 2 中,没有运算符与
real
和 numeric
完全匹配,所以我希望这里不会发生任何事情。
3a。丢弃输入类型不匹配且无法转换(使用隐式转换)来匹配的候选运算符。
我预计这会丢弃大多数候选人。 它应该保留恰好两个:一个用于
real
,另一个用于显然最终使用的double precision
。 (请注意,real
不能隐式转换为 numeric
,因此不应保留 numeric
。)
3b。如果任何输入参数属于域类型,则在所有后续步骤中将其视为域的基本类型。
我希望这不会执行任何操作,因为这里没有域类型。
3c。遍历所有候选者并保留那些与输入类型最精确匹配的候选者。如果没有完全匹配的候选者,则保留所有候选者。如果只剩下一名候选者,则使用它;否则继续下一步。
在这里,我希望它应该丢弃
double precision
的运算符,因为 real
的运算符有 1 个精确匹配,而 double precision
的运算符有 0 个精确匹配。 然后,由于这里只剩下一个候选者,它应该使用 real
运算符,我预计结果是 real
。 但相反,结果是一个 double precision
值。
所以,我的问题是,为什么 PostgreSQL 在这种情况下选择
double precision
运算符,它需要隐式转换两个操作数而不是仅其中之一? 是文档错误,还是我的理解错误?
拼图中缺失的两块是:
涉及多个参数数据类型的调用,例如
+integer
,可通过使用这些列表后面出现的类型来解析。numeric
相关列表为“
smallint
、integer
、bigint
、numeric
、real
和double precision
”。
pg_type.ispreferred
中检查类型偏好。在那里,float8
(double precision
) 优于 float4
(real
) 以及 numeric
。
select typname
, typispreferred
from pg_type
where typcategory='N'
order by typispreferred desc
,typname
类型名称 | 典型首选 |
---|---|
浮动8 | T |
类 | T |
基数数字 | f |
浮动4 | f |
int2 | f |
int4 | f |
int8 | f |
钱 | f |
数字 | f |
前者只是为了使这个答案更通用并适用于其他组合。后者正是为什么您看到
real+numeric
结果是 real+numeric::double precision
产生 double precision
,而不是 real+numeric::real
会给您带来 real
。