问题描述
一段时间以来,我一直想添加一个安全除法函数来帮助那些没有花太多时间编写 sql 查询的团队成员。我刚刚注意到 PG 13 似乎添加了一个 anycompatible
伪类型。我已经试过了,这似乎按希望和预期的那样工作。我以前没有使用过伪类型,我想知道我是否正在涉足一些我可能没有预料到的风险和限制。有人要提供一些警告吗?
下面列出了函数代码,并附有一个快速 select
以说明其在某些情况下的行为。
CREATE OR REPLACE FUNCTION tools.div_safe(
numerator anycompatible,denominator anycompatible)
RETURNS real
AS $BODY$
SELECT numerator/NULLIF(denominator,0)::real
$BODY$
LANGUAGE sql;
COMMENT ON FUNCTION tools.div_safe (anycompatible,anycompatible) IS
'Pass in any two numbers that are,or can be converted to,numbers,and get a safe division real result.';
ALTER FUNCTION tools.div_safe (anycompatible,anycompatible)
OWNER TO user_bender;
样本选择和输出:
-- (real,int))
select '5.1/nullif(null,0)',5.1/nullif(null,0) as result union all
select 'div_safe(5.1,div_safe(5.1,0) as result union all
-- (0,0)
select '0/nullif(0,0) as result union all
select 'div_safe(0,div_safe(0,0) as result union all
-- (int,int)
select '5/nullif(8,0)::real',5/nullif(8,0)::real as result union all
select 'div_safe(5,8)',div_safe(5,8) as result union all
-- (string,int)
select 'div_safe(''5'',div_safe('5',8) as result union all
select 'div_safe(''8'',5)',div_safe('8',5) as result union all
-- Rounding: Have to convert real result to numeric to pass it into ROUND (numeric,integer)
select 'round(div_safe(10,3)::numeric,2)',round(div_safe(10,2) as result
+-----------------------------------+-------------------+
| ?column? | result |
+-----------------------------------+-------------------+
| 5.1/nullif(null,0) | NULL |
| div_safe(5.1,0) | NULL |
| 0/nullif(0,0) | NULL |
| div_safe(0,0) | NULL |
| 5/nullif(8,0)::real | 0.625 |
| div_safe(5,8) | 0.625 |
| div_safe('5',8) | 0.625 |
| div_safe('8',5) | 1.600000023841858 |
| round(div_safe(10,2) | 3.33 |
+-----------------------------------+-------------------+
解决方法
我认为您的使用没有问题。在这种情况下,您也可以使用 anyelement
,它适用于旧版本。
如果您总是想要 real
结果,您的函数很好。如果您希望结果与参数具有相同的类型,请对结果也使用伪类型。