regexp_matches 在 plpgsql 函数中返回 NULL

Posted

技术标签:

【中文标题】regexp_matches 在 plpgsql 函数中返回 NULL【英文标题】:regexp_matches returns NULL inside plpgsql function 【发布时间】:2013-02-06 02:01:01 【问题描述】:

给定一个文件名:

xxxx/2013-02/csv/Sales_1302040000-1302050000.zip

谁能解释一下为什么 regexp_matches 在这个函数中返回 null:

CREATE OR REPLACE FUNCTION get_import_batch_date(filename text) 
RETURNS DATE AS
$BODY$    
DECLARE
    matches text[];
    result date;
BEGIN

    matches := regexp_matches(filename, E'Sales_(\\d2)(\\d2)(\\d2)');    
    IF matches IS NOT NULL THEN
        result := format('%s-%s-%s', 2000 + matches[1]::int, matches[2], matches[3])::DATE;
        RETURN result;
    END IF;

    RAISE WARNING 'Unable to determine batch date from %', filename;

    RETURN NULL;

END;
$BODY$
  LANGUAGE plpgsql IMMUTABLE;

然而,在以下匿名函数中工作:

DO language plpgsql $$
DECLARE
    filename text := 'xxxx/2013-02/csv/Sales_1302040000-1302050000.zip';
    matches text[];
    result date;
BEGIN

    matches := regexp_matches(filename, E'Sales_(\\d2)(\\d2)(\\d2)');    
    IF matches IS NOT NULL THEN
        result := format('%s-%s-%s', 2000 + matches[1]::int, matches[2], matches[3])::DATE;
        raise notice '%', result;
    END IF;

END;
$$;      

并且 regexp_matches 在这个查询中似乎可以正常工作,但同样,该函数失败并返回 null

SELECT
    regexp_matches('xxxx/2013-02/csv/Sales_1302040000-1302050000.zip', E'Sales_(\\d2)(\\d2)(\\d2)'),
    get_import_batch_date('xxxx/2013-02/csv/Sales_1302040000-1302050000.zip');

我的代码中是否有我没有看到的错误(很可能也是最常见的答案)或者我在这里没有做些什么?

我使用的是 PostgreSQL 9.1.6

最后一点:给定这个文件名,我希望函数返回 2013-02-04 的日期值

【问题讨论】:

【参考方案1】:

更新:

问题原来是在 pgAdmin 中对 pgScript 的混淆。 @David 在 pgAdmin 的查询工具中按 F6 来运行 pgScript 而不是 F5 来运行 SQL 脚本。请参阅comments below。 功能本身很好。

简化函数

我无法重现您的错误(在 Postgres 9.1.6 上测试,未返回 NULL),但我可以为您提供一个更简单的函数版本,可能不会失败:

CREATE OR REPLACE FUNCTION get_import_batch_date(filename text, OUT result date)
  AS
$func$    
BEGIN
   result := ('20' || substring(filename, E'Sales_(\\d6)'))::date;

   IF result IS NULL THEN
      RAISE WARNING 'Unable to determine batch date from %', filename;
   END IF;
END
$func$ LANGUAGE plpgsql IMMUTABLE;

使用OUT 参数来简化事情。

不需要相当复杂的regexp_matches() 表达式和它所需要的数组转换。 一个简单的substring() 电话就可以完成这项工作。预先添加20,然后您就可以立即转换为date。该格式匹配在 any 区域设置中有效的 ISO 8601 日期格式。您的原始版本也依赖于此,只是添加了连字符 (-),这是可选的。

 `'20130204'::date` works just as well as `'2013-02-04'::date`
不需要RETURN,自动返回OUT参数result的值。

【讨论】:

谢谢欧文;你的回答总是很棒。但是,这里可能是一个愚蠢的问题。如果我想在更新语句中设置字段值,如何使用该函数? @DavidS: 喜欢UPDATE tbl SET date_col = get_import_batch_date('mystring...') WHERE tbl_id = ? ..? 哦。我懂了。我现在觉得很笨。我想它就像我在 Pascal 中(多年前)做的 params 一样,我需要一个变量来传递它。但是,无论哪种情况,它都不适合我。我不知道为什么。我准确地剪切并粘贴了代码。也许是时候看电影了。 顺便说一句,感谢您教我 ISO 8601。我认为它必须采用“YYYY-MM-DD”的格式。肯定在那里学到了一些东西。 @DavidS:别傻了,这是一个问答网站。我的新版本也不适合你?【参考方案2】:

也可以在这里工作:http://sqlfiddle.com/#!1/d084b/1

您确定这正是传递给 get_import_batch_date 的文件名吗?

【讨论】:

是的;我确信。我复制并粘贴了它,并检查了三次。甚至喝了一杯咖啡,让我的视线暂时离开了它。用上面的代码创建函数是否有效? 哇!是的;它适用于你的 sqlfiddle。但是,我剪切并粘贴了该代码,它对我不起作用。 $%#@ 是什么?似乎它必须是我正在做的事情。由于您所做的工作,给了您并投票。谢谢!【参考方案3】:

好的!我终于弄明白了。我不确定为什么会发生这种情况,或者发生了什么,但我至少可以解决它。我在这里发布的答案实际上是基于 Erwin 的答案。他的代码(和往常一样)比我的要好得多,但如果其他人将来遇到这个非常令人沮丧的问题,这将有效。

基本上,我今晚又在玩它,它终于引起了我的注意。如果我采用此代码:

CREATE OR REPLACE FUNCTION get_import_batch_date(in filename text, out result date) AS
$BODY$
DECLARE
BEGIN
   result := substring(filename, E'Sales_(\\d6)')::date;
   IF result IS NULL THEN
      RAISE WARNING 'Unable to determine batch date from %', filename;
   END IF;   
END
$BODY$
  LANGUAGE plpgsql IMMUTABLE
  COST 100;

...然后按 F6 键“运行脚本”,您会收到以下消息:

[QUERY    ] CREATE OR REPLACE FUNCTION get_import_batch_date(in filename text, out result date) AS
            $BODY$
            DECLARE
            BEGIN
               result := substring(filename, E'Sales_(\d6)')::date;
               IF result IS NULL THEN
                  RAISE WARNING 'Unable to determine batch date from %', filename;
               END IF;   
            END
            $BODY$
              LANGUAGE plpgsql IMMUTABLE
              COST 100

你能发现关键问题吗?我昨晚不能,但今晚做了。它正在剥离子字符串函数上的“\”之一。

这将导致匹配失败并返回 NULL。

如果您按 F5 或单击该功能的“运行”按钮,则它可以正常工作。 (这可能是人们正在做的事情,或者可能是 SQLFiddle 正在做的事情(这里完全猜测)。

为了让 F6 为我工作,我必须将行更改为:

   result := substring(filename, E'Sales_(\\\d6)')::date;

所以,这对我有用。这感觉就像某个地方的错误。但是,我不知道在哪里。也许@Erwin 可以对此有所了解。

【讨论】:

解释比较简单。准备好你的额头被打耳光。 :) 在pgAdmin(你从来没有提到过!!)F6 是执行pgScript 的键盘快捷键,它使用自己的语法规则作为元语言。签出:pgadmin.org/docs/1.16/pgscript.html#pgscript 是的;真的很奇怪。我没有提到它,因为我从没想过它会有所作为。现在我知道得更清楚了。 :) 昨晚快把我逼疯了。我不明白为什么其他人都在工作。我敢打赌,我剪切并粘贴了您的解决方案 3 或 4 次。 记得按 F5 或点击 pgAdmin 的 SQL 编辑器中的纯绿色箭头按钮来执行 SQL 命令。 :) 或者选择一些代码并点击F5 只执行突出显示的部分。 但是,当您因为签名已更改而需要删除该函数时怎么办?当我在处理一个函数时,我总是有一个“下降”。或者您需要更换所有者?我只有一个脚本来执行此操作并按 F6。我不知道它可以以不同的方式解析。我会这么说,可能会有一个解释,但这仍然感觉非常错误。可以根据执行方式创建函数(并返回完全不同的结果)。我明白了,但感觉还是很糟糕。 慢下来......你在这里混合了至少 4 种不同的东西。 1.) 所有者无关紧要。 2.) 如果 函数签名 没有改变,你可以用CREATE OR REPLACE FUNCTION ... 更新一个函数,否则你必须删除并重新创建它 3.) PostgreSQL 允许function overloading。 4.) 这一切都与 pgScript 无关,它是 pgAdmin 的一个特性(我几乎从不使用。)

以上是关于regexp_matches 在 plpgsql 函数中返回 NULL的主要内容,如果未能解决你的问题,请参考以下文章

Google Big Query 中 REGEXP_MATCH 的奇怪行为

BigQuery 为 REGEXP_MATCH 或 _EXTRACT 返回 null

带有数字通配符的 UCanAccess Select 语句给出了意外的令牌:需要 REGEXP_MATCHES

如何在plpgsql中读写psql变量

在plpgsql中循环数组维度

在plpgsql中获取光标