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