4

我有一张表,其中的条目类似于“某事 XXX010101 某事 XXX010102”。

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

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

我的一个想法是创建一些怪物正则表达式来替换不是 XXX 子字符串的所有内容......但说实话,我希望(但未能找到)一个更简单的解决方案(比如将 ag 标志转换为子字符串)。

环顾四周时,我发现 8.3 引入了一个似乎是我需要的 regex_matches 函数 - 8.2 中是否有任何替代方案?还是在 8.2 中获得它的简单方法?

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

提前致谢。

-- 添加了预期输出 --

为了

"something XXX010101 something else XXX010102"

我想得到:

XXX010101
XXX010102

或(不太可取)

XXX010101,XXX010102 
4

3 回答 3

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

一些解释:

  • regex_matches()如 OP 所述,版本 8.2 中没有
  • 但是有regexp_replace()哪个可以使用g标志(局部替换g
  • 我们不能在同一个正则表达式中混合贪婪和非贪婪量词
  • ,因此,在删除所有其他出现的,可能不是所需字符串的一部分但,可以用作结果中的分隔符的所有其他出现之后,我用 a 终止所需的字符串。
  • 首切前导和尾随噪声
  • 然后全局替换所需字符串之间的所有内容。
  • 使该工作用于(XXX\\d+?,)*?捕获所需字符串的任意数量的主要出现。
  • finalrtrim()删除尾随,

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

这大量使用了 PostgreSQL 的POSIX 正则表达式的功能(链接到 8.2 版!)

于 2011-12-02T17:28:34.713 回答
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', '')

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

于 2011-12-02T12:38:39.170 回答
0

最快的方法是使用适用于 8.2 的 plperlu。

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。

于 2017-03-10T06:27:12.827 回答