在PostgreSQL中获取字符串中所有子串的实例?

时间:2011-12-02 12:30:03

标签: sql postgresql postgresql-8.2

我的表格中的条目类似于“XXX010101 somethingelse XXX010102”。

我想从中提取XXX01 ...部分 - 如果需要可能会使用一些分隔符 - 现在我可以轻松地使用以下内容取出第一个:

select distinct substring(content from 'XXX[0-9]+'), content from data where content ~ 'XXX[0-9]+'

我的一个想法就是创建一些怪物正则表达式来替换不是XXX子串的所有内容......但是为了更简单的解决方案(如ag标记到子串),我希望(并且找不到)。 / p>

环顾四周时,我发现8.3引入了一个regex_matches函数,这似乎是我需要的东西 - 8.2中有什么替代品吗?或者在8.2中轻松获得它?

你会如何解决这个问题?或者升级是我最好的选择? ;)(这是一个生产系统,因此停机后的停机时间和一些问题的风险是犹豫的来源)。

提前致谢。

- 预期产量增加 -

代表

"something XXX010101 something else XXX010102"

我想得到:

XXX010101
XXX010102

OR(不太可取)

XXX010101,XXX010102 

3 个答案:

答案 0 :(得分:2)

我甚至对发布我的答案犹豫不决。你真的必须升级。版本8.2现在即将结束。点击链接@a_horse_with_no_name发布。

然而,问题引起了我的注意。以下演示适用于 PostgreSQL 8.2

SELECT -- content,
         rtrim(
         regexp_replace(
         regexp_replace(
         replace(
         substring(content
        ,E'(XXX\\d+|XXX\\d+.*XXX\\d+)')  -- kill leading / trailing noise
        ,',',' ')                        -- remove all ","
        ,E'(XXX\\d+)', E'\\1,', 'g')     -- terminate X-strings with ","
        -- now we can use non-greedy terminated with ","
        ,E'(XXX\\d+?,)*?.*?(XXX\\d+?,)', E'\\1\\2', 'g')
        ,',') AS result
FROM    (VALUES
  ('no match')
 ,('XXX010101')
 ,('XXX010102 beginn')
 ,('end XXX010103')
 ,('middle XXX010104 match')
 ,('match middle XXX010105 and end XXX010106')
 ,('XXX010107, match beginn XXX010108 & middle')
 ,('XXX010109 begin and end XXX010110')
 ,('XXX01011 begin XXX010112 middle and end XXX010113')
 ,('XXX01014 begin XXX010115 close matches XX010113 XXXy010113 23624 ,XXX010116')
 ,('f XXX01017 B XXX010118 457XXX010119 XXXy XXX010120 overkill XXX010121end')
) data(content)

结果:

                     result
--------------------------------------------------
             -- first line is NULL
 XXX010101
 XXX010102
 XXX010103
 XXX010104
 XXX010105,XXX010106
 XXX010107,XXX010108
 XXX010109,XXX010110
 XXX01011,XXX010112,XXX010113
 XXX01014,XXX010115,XXX010116
 XXX01017,XXX010118,XXX010119,XXX010120,XXX010121

一些解释:

  • 在版本8.2中没有regex_matches(),如OP已经陈述的
  • 但有regexp_replace()可以使用g标记(以g为标题)
  • 我们不能在同一个正则表达式中混合使用贪婪和非贪婪的量词
  • 所以我在移除,的所有其他出现后,使用,终止所需的字符串可能是任何不属于所需字符串的字符,但,可以作为分隔符结果。
  • 首先削减前方噪音和尾随噪音
  • 然后全局替换所需字符串之间的所有内容。
  • 使该工作使用(XXX\\d+?,)*?来捕获任意数量的有用字符串。
  • final rtrim()删除尾随,

  • 在PsotgreSQL 8.3+中,您可以使用regexp_split_to_table()将所需的字符串拆分为单行。在8.2你必须提出自己的东西。我会写一个plgpsql函数......

这大量使用了PostgreSQL POSIX Regular Expressions的功能(链接到8.2版本)。

答案 1 :(得分:1)

这样的事情怎么样(假设你要找的值都包含在一个单独的表格中)......虽然我不会声称表现良好......

Select A.text, B.Text2, B.Val 
FROM A 
INNER JOIN B ON B.Text2 LIKE ('%' || A.Text || '%')

让表格A包含您正在寻找的所有可能的XXX010101组合

text
XXX010101
XXX010102
XXX010103

让表B包含您想要的所有搜索文本

text 2                                              val
something XXX010101 something else XXX010102        1
yet another XXX010102 and this XXX010103            2
XXX010105                                           3
XXX010103                                           4

结果:

text            text2                                           VAL
XXX010101   something XXX010101 something else XXX010102    1
XXX010102   something XXX010101 something else XXX010102    1
XXX010102   yet another XXX010102 and this XXX010103        2
XXX010103   yet another XXX010102 and this XXX010103        2
XXX010103   XXX010103                                       4

--------错误但误解了这个问题......

更换功能有什么问题? http://www.postgresql.org/docs/8.2/interactive/functions-string.html

replace( 'abcdefabcdef', 'cd', '')

所以字符设置为空字符串。

答案 2 :(得分:0)

最快的方法是使用plperlu which works with 8.2.

CREATE LANGUAGE plperl

CREATE FUNCTION get_things(inputStr text)
RETURNS SETOF text
AS $BODY$
  return \@{[ $_[0] =~ m/(XXX\d{6})/g ]} 
$BODY$
LANGUAGE plperl
IMMUTABLE;

SELECT get_things(x)
FROM ( VALUES
  ('XXX010101 somethingelse XXX010102')
) AS t(x);

 get_things 
------------
 XXX010101
 XXX010102
(2 rows)

它也适用于PostgreSQL的新版本。