使用可变参数、任意数组和任意元素移植 Oracle decode()

port Oracle decode() using variadic, anyarray, and anyelement

我需要从 Oracle 移植一个广泛使用 decode() 的存储过程。也就是说,我不能像 guide 所建议的那样使用 CASE WHEN expr THEN expr [...] ELSE 系列。

我想创建一个可变参数函数,但问题是:在 Oracle 中,该函数可以接受任意数量的键值对,并且键的类型不一定与值的类型匹配:

select decode(0  ,0,'a'  ,1,'b'  ,2,'c'  /*,...*/ ,'dflt') from dual;

我尝试使用 anyarray:

create or replace function decode(VARIADIC args anyarray) RETURNS text AS $$
  SELECT null::text;
$$ LANGUAGE SQL;

但这只适用于所有参数都是同一类型的情况:

select decode(0,0,0); -- ok
select decode('x'::text,'x'::text,'x'::text); -- ok
select decode(0,0,'a'::text); -- No function matches the given name and argument types

如果无法使用所需的语法,请建议另一种传递 expr、成对集合和默认值的方法,同时保持它们在 Oracle 中的位置相同。

限制

看来这就是 PostgreSQL 的实现方式。读取 docs:

35.4.5. SQL Functions with Variable Numbers of Arguments

SQL functions can be declared to accept variable numbers of arguments, so long as all the "optional" arguments are of the same data type. The optional arguments will be passed to the function as an array. The function is declared by marking the last parameter as VARIADIC; this parameter must be declared as being of an array type.

JSON

如果您找到一种方法将您的混合数组从 Oracle 导出为 JSON 格式,那么 PostgreSQL JSON type 将处理它:

CREATE OR REPLACE FUNCTION xdecode(data json)
RETURNS TEXT AS
$BODY$
    -- Your implementation here
    SELECT NULL::TEXT;
$BODY$ LANGUAGE SQL;

这个函数接受一个 JSON 字符串,它可以是这样的:

SELECT xdecode('[1, 2, 3.3, "a", true, null]'::json);

Table类型

如果要解码的参数元组匹配 TABLE 类型,则可以使用它:

CREATE TABLE foo(
    x INTEGER,
    y FLOAT,
    z TEXT
);

CREATE OR REPLACE FUNCTION xdecode2(data foo)
RETURNS TEXT AS
$BODY$
    SELECT row_to_json(data)::TEXT;
$BODY$ LANGUAGE SQL;

那么这个调用有效:

SELECT xdecode2((1, 2.1, 'x'))

遗憾的是,我们不能使用泛型 RECORD 类型作为函数的输入参数。

CREATE OR REPLACE FUNCTION xdecode3(data RECORD)
RETURNS TEXT AS
$BODY$
    SELECT row_to_json(data)::TEXT;
$BODY$ LANGUAGE SQL;

加薪:

ERROR: SQL functions cannot have arguments of type record
SQL state: 42P13

类型anyelement

正如@basin 所指出的,可以使用 anyelement:

来模拟类型 RECORD
CREATE OR REPLACE FUNCTION xdecode4(data anyelement)
RETURNS TEXT AS
$BODY$
    SELECT row_to_json(data)::TEXT;
$BODY$ LANGUAGE SQL;

SELECT xdecode4((1, 2.1, 'x'));

Returns:

'{"f1":1,"f2":2.1,"f3":"x"}'