2

例如,假设我想在 postgres 中编写一个自定义函数来安全地除以两个数字——即它应该检查任何一个参数是否为空以及除数是否为零。它应该优雅地处理这些错误情况,否则返回预期的商。

只要两个参数是相同的数字类型(例如,两个整数、两个数字等),当前代码就可以正常工作。

CREATE OR REPLACE FUNCTION safe_divide(anyelement, anyelement) RETURNS numeric AS $$
  SELECT CASE 
     WHEN $1 IS NULL OR $2 IS NULL OR $2 = 0 THEN NULL 
     ELSE $1::numeric / $2::numeric
     END;
$$ LANGUAGE SQL;

我的问题:有没有办法编写这个函数,以便我可以提供不同的数字类型?

我想避免:

  • 调用函数时需要显式转换参数(例如safe_divide(x::numeric, y::numeric)

  • 需要为每种可能的数据类型定义函数

谢谢!

4

3 回答 3

4

如果你用数字参数和双精度定义你的函数,那么你可以将它用于每个数字参数。

  CREATE OR REPLACE FUNCTION safe_divide(numeric, numeric) RETURNS numeric AS $$
   SELECT CASE 
     WHEN $1 IS NULL OR $2 IS NULL OR $2 = 0 THEN NULL ELSE $1 / $2 END;
  $$ LANGUAGE SQL;

  CREATE OR REPLACE FUNCTION safe_divide(double precision, double precision)
  RETURNS numeric AS $$
   SELECT CASE 
     WHEN $1 IS NULL OR $2 IS NULL OR $2 = 0 THEN NULL 
                                             ELSE $1::numeric / $2::numeric END;
  $$ LANGUAGE SQL;

它对其他类型的数据类型没有意义

 postgres=# select safe_divide(10::float,10::int);
       safe_divide       
 ------------------------
  1.00000000000000000000
 (1 row)

 postgres=# select safe_divide(10::numeric,10::int);
       safe_divide       
 ------------------------
  1.00000000000000000000
 (1 row)

 postgres=# select safe_divide(10::int,10::int);
       safe_divide       
 ------------------------
  1.00000000000000000000
 (1 row)

 postgres=# select safe_divide(10,10.0);
       safe_divide       
 ------------------------
  1.00000000000000000000
 (1 row)

这是 PostgreSQL 中的典型模式

于 2013-04-23T05:54:26.390 回答
0

恐怕这是不可能的:

声明为 anyelement 的每个位置(参数或返回值)都允许具有任何特定的实际数据类型,但在任​​何给定的调用中,它们都必须是相同的实际类型。 http://www.postgresql.org/docs/current/static/extend-type-system.html

我认为最好的方法是将参数转换为数字。

于 2013-04-22T09:18:12.083 回答
0

我希望这也会对某人有所帮助。

感谢Pavel Studule引导我回答这个问题。

它确实使代码干燥,但要获得覆盖,您仍然需要定义它们。

在某些情况下,您可以定义一个包含主要工作的原始函数,然后创建调用原始函数并在函数中转换值的新函数定义。

CREATE OR REPLACE FUNCTION last_day_in_month(dt date) RETURNS integer AS $$
BEGIN
  RETURN DATE_PART('days',
    DATE_TRUNC('month', dt)
    + '1 MONTH'::INTERVAL
    - '1 DAY'::INTERVAL
  );
END; $$
LANGUAGE PLPGSQL;

CREATE OR REPLACE FUNCTION last_day_in_month(dt timestamp) RETURNS integer AS $$
BEGIN
  RETURN last_day_in_month(dt::DATE);
END; $$
LANGUAGE PLPGSQL;
于 2019-07-30T21:01:23.527 回答