在 Postgres 13 中使用 anycompatible 伪类型的安全除法函数,有问题吗?
Safe division function using the anycompatible pseudo-type in Postgres 13, gotchas?
一段时间以来,我一直想添加一个安全除法函数,以帮助那些没有花太多时间编写 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;
示例 select 并输出:
-- (real, int))
select '5.1/nullif(null,0)', 5.1/nullif(null,0) as result union all
select 'div_safe(5.1,0)', div_safe(5.1, 0) as result union all
-- (0, 0)
select '0/nullif(0,0)', 5.1/nullif(null,0) as result union all
select 'div_safe(0, 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'',8)', 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,3)::numeric, 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,3)::numeric, 2) | 3.33 |
+-----------------------------------+-------------------+
我认为您的使用没有问题。在这种情况下,您也可以使用 anyelement
,它适用于旧版本。
如果你总是想要 real
结果,你的功能就很好。如果您希望结果与参数具有相同的类型,请对结果也使用伪类型。
一段时间以来,我一直想添加一个安全除法函数,以帮助那些没有花太多时间编写 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;
示例 select 并输出:
-- (real, int))
select '5.1/nullif(null,0)', 5.1/nullif(null,0) as result union all
select 'div_safe(5.1,0)', div_safe(5.1, 0) as result union all
-- (0, 0)
select '0/nullif(0,0)', 5.1/nullif(null,0) as result union all
select 'div_safe(0, 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'',8)', 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,3)::numeric, 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,3)::numeric, 2) | 3.33 |
+-----------------------------------+-------------------+
我认为您的使用没有问题。在这种情况下,您也可以使用 anyelement
,它适用于旧版本。
如果你总是想要 real
结果,你的功能就很好。如果您希望结果与参数具有相同的类型,请对结果也使用伪类型。