我正在尝试在PostgreSQL 9.0.1中进行稍微复杂的字符串转换。 my_col
中的值是长字符串,格式为:
'12345_sometext_X12B_1'
'12345_sometext_optionaltext_Y09B_1'
'12345_sometext_optionaltext_X12A_1'
我需要将'X12'部分转换为已知的数值,有几个不同的已知值(最多5个)。
我希望能够在一个查询中确定这一点,而无需子查询。但是,以下内容对我不起作用。最后一列是抛出异常的那一列。由于某些原因,似乎我无法使用这些函数的输出来执行CASE
语句。我已经将进行中的列仅用于演示目的。
select
regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$'), -- returns {'X12'}
(regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$'))[1], -- returns 'X12'
case (regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$'))[1]
when 'X12' then '1200'
when 'Y09' then '950'
else '?' end -- should return '1200' but throws error
from my_table;
相反,我得到了错误:
ERROR: set-valued function called in context that cannot accept a set
SQL state: 0A000
有人可以告诉我吗?
答案 0 :(得分:8)
鉴于数据:
create table my_table(my_col text);
insert into my_table(my_col) values
('12345_sometext_X12B_1'),
('12345_sometext_optionaltext_Y09B_1'),
('12345_sometext_optionaltext_X12A_1'),
('nomatch');
上述查询确实会产生您报告的错误。非常奇怪,因为:
SELECT pg_typeof((regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$'))[1]);
返回'text'。它应该真的说setof text
,这就是陷阱:regex_matches
是一个集合返回函数。那些在PostgreSQL中从FROM子句外部调用时有......有趣......行为。
regexp_matches函数返回所有的文本数组 捕获由匹配POSIX正则表达式产生的子字符串 图案。它具有语法regexp_matches(string,pattern [,flags])。 该函数不能返回任何行,一行或多行
尝试重新构造查询以使用子查询来调用SRF。如果匹配器返回多行,则会失败:
SELECT
CASE (SELECT x[1] FROM regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$') x)
WHEN 'X12' THEN '1200'
WHEN 'Y09' THEN '950'
ELSE '?'
END
FROM my_table;
想看看SELECT中有多奇怪的SRF在Pg中?比较这些查询的结果:
SELECT generate_series(1,10), generate_series(1,15);
和
SELECT generate_series(1,10), generate_series(1,20);
第1行产生30行。第二个产生20.有趣解释原因。如果偶尔有用的结果,Pg中SELECT列表中的多个SRF会产生疯狂。
PostgreSQL 9.3支持SQL标准LATERAL
子句,感谢Tom Lane,它为当前行为提供了一个明智且定义明确的替代方案。
答案 1 :(得分:2)
regexp_matches()
返回SETOF text[]
( 文本数组 ),这对于同一字符串中的一个模式的多个匹配很有用。但它只是这项任务的错误工具。
使用substring()
代替正则表达式。它返回text
。重用demo table in @Craig's answer:
SELECT CASE substring(my_col, '^.*_([^_]*)[A-Z]_\d*$')
WHEN 'X12' THEN '1200'
WHEN 'Y09' THEN '950'
ELSE '?'
END As result
FROM my_table;
返回:
result
--------
1200
950
1200
?
还略微简化了正则表达式。 {1}
只是噪音。
如果你需要优化性能,请尝试不使用正则表达式,这些表达式功能强大但价格相对较高。类似的东西:
reverse(right(split_part(reverse(my_col), '_', 2), -1))
看起来更复杂,但在我的测试中仍然更快。