【问题标题】:regexp_matches returns NULL inside plpgsql functionregexp_matches 在 plpgsql 函数中返回 NULL
【发布时间】: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_(\\d{2})(\\d{2})(\\d{2})');    
    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_(\\d{2})(\\d{2})(\\d{2})');    
    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_(\\d{2})(\\d{2})(\\d{2})'),
    get_import_batch_date('xxxx/2013-02/csv/Sales_1302040000-1302050000.zip');

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

我使用的是 PostgreSQL 9.1.6

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

【问题讨论】:

    标签: regex postgresql pattern-matching plpgsql postgresql-9.1


    【解决方案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_(\\d{6})'))::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_(\\d{6})')::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_(\d{6})')::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_(\\\d{6})')::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 的一个特性(我几乎从不使用。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-30
    • 1970-01-01
    • 1970-01-01
    • 2021-07-06
    • 1970-01-01
    相关资源
    最近更新 更多