regexp_matches在plpgsql函数内返回NULL

时间:2013-02-06 02:01:01

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

给出文件名:

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

3 个答案:

答案 0 :(得分:2)

更新

问题结果是对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。格式符合ISO 8601日期格式,该格式在任何区域设置中有效。您的原始版本也依赖于此,只需添加连字符(-),这是可选的。

    `'20130204'::date` works just as well as `'2013-02-04'::date`
    

  • 不需要RETURNOUT参数result的值会自动返回。

答案 1 :(得分:1)

也适用于此:http://sqlfiddle.com/#!1/d084b/1

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

答案 2 :(得分:0)

确定!我终于弄明白了。我不确定为什么会发生这种情况,或者发生了什么,但我至少可以解决它。我在这里发布的答案实际上是基于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可以对此有所了解。