【问题标题】:Handling Infinity and NaN in Snowflake在 Snowflake 中处理 Infinity 和 NaN
【发布时间】:2020-09-21 16:17:56
【问题描述】:

我正在将数据仓库前端从 Hadoop 后端转换为 Snowflake 后端,我需要将其每个 SQL 函数转换为等效的 Snowflake。我遇到的一件事是处理产生 Infinity/-Infinity 或 NaN(非数字)的公式。

在 Hadoop 中,这只是使用 IS_INF 和 IS_NAN 检查公式的简单问题。在 SQL Server(我们测试的另一个后端)中,这可以使用 SET ARITHABORT OFF/SET ANSI_WARNINGS OFF 来完成。 Hadoop 方法向 SQL 添加了额外的逻辑检查,而 SQL Server 方法直接忽略了错误并返回 NULL,而不是引发错误。我更喜欢后一种方法,因为它不会减慢查询速度,但我愿意接受任何可以在 Snowflake 中实际工作的方法,因为到目前为止我还找不到。

我应该注意到,有一些雪花函数可以帮助避免非常具体的错误,例如除以零(即 DIV0 函数或 NULLIF),但这对我没有帮助有两个原因。首先,这只处理某些问题(除以零)。我需要能够处理任何产生 Inf/NaN 的公式的东西。其次,有问题的公式通常是由最终用户在前端编写的,这意味着我不知道公式的哪个部分是被除数,哪个是除数。公式可能非常复杂(包括 CASE WHEN 或其他函数或任何数量的东西),所以解析除数(所以我可以使用 NULLIF),或者解析除数和除数(所以我可以使用 DIV0)是'不是一个真正的选择。

我注意到 Snowflake 的文档说 FLOAT 数据类型可以处理 Infinity 和 NaN 等特殊值,但我没有看到任何方法可以在公式中实际生成这些值而不会引发错误。

任何帮助将不胜感激。

【问题讨论】:

    标签: snowflake-cloud-data-platform


    【解决方案1】:

    (据我所知)没有用于 Infinite NaN 值的内置便利函数。尤其是没有达到您似乎正在寻求的程度。在我自己的工作中,我不必处理数字无穷大成为问题的情况,但是我们可以通过创建自己的函数来实现至少一些功能。在这种情况下,我们很可能只使用 SQL-UDF,而 Javascript 则具有更大的灵活性。
    下面我为此目的定义了 4 个函数(IS_INFIS_NAIF_NAIF_INF)。请注意,这些函数接受变体输入并返回变体输出。这样我就不必为所有不同的data types 重载函数。在实践中,这仅对varchar 和派生类型(日期、时间戳)以及可能的地理空间数据存在问题,这些数据必须转换为 VARIANT。

    CREATE FUNCTION IS_INF(x VARIANT)
    RETURNS boolean
    AS 
    $$
        iff(try_cast(x::varchar as float) IN ('Inf', -'Inf'), TRUE, FALSE)
    $$;
    CREATE FUNCTION IS_NA(x VARIANT)
    RETURNS boolean
    AS 
    $$
        iff(try_cast(x::varchar as float) = 'NaN', TRUE, FALSE)
    $$;
    CREATE FUNCTION IF_NA(x VARIANT, y VARIANT, z VARIANT)
    RETURNS VARIANT 
    AS 
    $$
        iff(try_cast(x::varchar as float) = 'NaN', y, z)
    $$;
    CREATE FUNCTION IF_INF(x VARIANT, y VARIANT, z VARIANT)
    RETURNS VARIANT 
    AS 
    $$
        iff(try_cast(x::varchar as float) IN ('Inf', -'Inf'), y, z)
    $$;
    

    请注意,这要求它们存储在特定架构中,因此如果希望在另一个架构或数据库中使用它,则必须选择特定架构。为此目的拥有一个数据库/模式通常是有帮助的。此外雪花确实允许IS_NAN,但之后我无法使用此功能。

    示例用法

    select is_inf(3245); -- false
    SELECT is_na('NaN'); -- true
    SELECT IF_inf(3245, 'NaN'::float, 4); -- 4
    SELECT if_na(3245, TRUE, FALSE); -- false
    

    【讨论】:

    • 这些需要能够处理公式,但可能包含 SQL 函数。
    • 你试过简单地传入一个表达式或sql函数吗?我希望(尽管未经测试)将评估结果传递给函数。例如。 is_inf(case when [...] then 'Inf' else [not inf thing] end) 可能会在没有进一步问题的情况下工作。
    • 我对此进行了测试,但不幸的是,此方法不适用于聚合函数,这是使用这些公式的主要方式。我还应该注意,try_cast 不会为除以零错误返回“Inf”或“-Inf”,它返回 NULL。除了除以零之外,它也不处理任何算术错误。例如, IS_NA(SQRT(-5)) 仍然会引发错误。但是,即使在创建了一个检查 try_cast(...) IS NULL 的 IF_DIV0 函数之后,当我尝试 IF_DIV0(SUM(dollars) / SUM(quantity)) 时它仍然不起作用。我得到了与往常一样的“除以零”错误。
    猜你喜欢
    • 2023-03-06
    • 2011-09-08
    • 1970-01-01
    • 2017-07-23
    • 1970-01-01
    • 2012-03-28
    • 1970-01-01
    • 2015-12-24
    • 2013-08-29
    相关资源
    最近更新 更多