PostgreSQL 替代 SQL Server 的 `try_cast` 函数

Man*_*ngo 13 postgresql datatypes error-handling cast

Microsoft SQL Server 有一个我认为非常明智的函数,如果转换不成功,try_cast()它返回一个null,而不是引发错误。

这使得可以使用CASE表达式或 acoalesce来回退。例如:

SELECT coalesce(try_cast(data as int),0);
Run Code Online (Sandbox Code Playgroud)

问题是,PostgreSQL 有没有类似的东西?

提出这个问题是为了填补我知识中的一些空白,但也有一个一般原则,即有些人更喜欢对某些用户错误做出不那么剧烈的反应。null在 SQL 中返回 a比错误更容易。例如SELECT * FROM data WHERE try_cast(value) IS NOT NULL;。根据我的经验,如果有 B 计划,有时可以更好地处理用户错误。

a_h*_*ame 13

如果从一种特定类型转换另一种特定类型就足够了,您可以使用 PL/pgSQL 函数执行此操作:

create function try_cast_int(p_in text, p_default int default null)
   returns int
as
$$
begin
  begin
    return $1::int;
  exception 
    when others then
       return p_default;
  end;
end;
$$
language plpgsql;
Run Code Online (Sandbox Code Playgroud)

然后

select try_cast_int('42'), try_cast_int('foo', -1), try_cast_int('bar')
Run Code Online (Sandbox Code Playgroud)

退货

try_cast_int | try_cast_int | try_cast_int
-------------+--------------+-------------
          42 |           -1 |             
Run Code Online (Sandbox Code Playgroud)

如果这仅适用于数字,另一种方法是使用正则表达式来检查输入字符串是否为有效数字。当您期望许多不正确的值时,这可能比捕获异常更快。


Erw*_*ter 12

基本原理

很难将SQL Server 之TRY_CAST类的东西包装到通用的 PostgreSQL 函数中。输入和输出可以是任何数据类型,但 SQL 是严格类型化的,并且 Postgres 函数要求在创建时声明参数和返回类型。

Postgres 有多态类型的概念,但函数声明最多接受一种多态类型。手册:

多态参数和结果相互关联,并在解析调用多态函数的查询时解析为特定的数据类型。声明为的每个位置(参数或返回值)anyelement都允许具有任何特定的实际数据类型,但在任​​何给定调用中,它们都必须是相同的实际类型。

CAST ( expression AS type )似乎是这条规则的一个例外,采用任何类型并返回任何(其他)类型。但它cast()只是看起来像一个函数,而它实际上是一个SQL 语法元素手册:

[...] 当使用两种标准强制转换语法之一进行运行时转换时,它将在内部调用已注册的函数来执行转换。

输入和输出类型的每种组合都有一个单独的函数。(您可以使用CREATE CAST...创建自己的)

功能

我的妥协是text用作输入,因为任何类型都可以转换为text. 额外的演员text意味着额外的成本(虽然不多)。多态性还增加了一些开销。但是中等成本的部分是我们需要的动态 SQL、涉及的字符串连接以及最重要的异常处理。

也就是说,这个小函数可用于任何类型的组合,包括数组类型。(但是像 in 这样的类型修饰符varchar(20)丢失了):

CREATE OR REPLACE FUNCTION try_cast(_in text, INOUT _out ANYELEMENT) AS
$func$
BEGIN
   EXECUTE format('SELECT %L::%s', $1, pg_typeof(_out))
   INTO  _out;
EXCEPTION WHEN others THEN
   -- do nothing: _out already carries default
END
$func$  LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

INOUT参数有_out两个用途:

  1. 声明多态类型
  2. 还带有错误情况的默认值

你不会像在你的例子中那样称呼它:

SELECT coalesce(try_cast(data as int),0);
Run Code Online (Sandbox Code Playgroud)

.. whereCOALESCE也从源 (!!) 中消除了真正的 NULL 值,可能不是预期的。但简单地说:

SELECT try_cast(data, 0);
Run Code Online (Sandbox Code Playgroud)

..NULLNULL输入或0无效输入时返回。

简短的语法在data是字符类型(如textvarchar)时有效,因为0是隐式类型为 的数字文字integer。在其他情况下,您可能需要更加明确:

示例调用

无类型字符串文字开箱即用:

SELECT try_cast('foo', NULL::varchar);
SELECT try_cast('2018-01-41', NULL::date);   -- returns NULL
SELECT try_cast('2018-01-41', CURRENT_DATE); -- returns current date
Run Code Online (Sandbox Code Playgroud)

类型值是已注册的隐式转换text工作开箱即用,太:

SELECT try_cast(name 'foobar', 'foo'::varchar);
SELECT try_cast(my_varchar_column, NULL::numeric);
Run Code Online (Sandbox Code Playgroud)

具有注册隐式强制转换为的数据类型的综合列表text

SELECT castsource::regtype
FROM   pg_cast
WHERE  casttarget = 'text'::regtype
AND    castcontext = 'i';
Run Code Online (Sandbox Code Playgroud)

所有其他输入类型都需要显式转换为text

SELECT try_cast((inet '192.168.100.128/20')::text, NULL::cidr);
SELECT try_cast(my_text_array_column::text, NULL::int[]));
Run Code Online (Sandbox Code Playgroud)

我们可以轻松地使函数体适用于任何类型,但函数类型解析失败。有关的:

  • 恒星的方法。 (2认同)