Получить все экземпляры подстроки в строке в PostgreSQL? - PullRequest
3 голосов
/ 02 декабря 2011

У меня есть таблица с записями, которые похожи на «что-то 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 

Ответы [ 3 ]

2 голосов
/ 02 декабря 2011

Я даже не решаюсь даже опубликовать свой ответ.Вы действительно должны обновить.Версия 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() в версии 8.2 в качестве OP уже указано
  • , но есть regexp_replace(), который может использовать флаг g (заменить g глобально)
  • мы не можем смешивать жадные и не жадные квантификаторыв том же регулярном выражении
  • , поэтому я заканчиваю нужные строки с помощью ,, после удаления всех других вхождений , Может быть любой символ, не являющийся частью нужных строк, но , может служить в качествев результате разделитель.
  • сначала обрежьте начальные и конечные помехи
  • , а затем глобально замените все, что находится между нужными строками.
  • , чтобы эта работа использовала (XXX\\d+?,)*? для захвата любого числаведущих вхождений искомой строки.
  • final rtrim() удаляет трейлинг ,

  • В PsotgreSQL 8.3+ вы можете использовать regexp_split_to_table() для разделенияхотел строки в один ряд.В 8.2 вы должны придумать что-то свое.Я бы написал функцию plgpsql ...

Это интенсивно использует возможности регулярных выражений POSIX PostgreSQL (ссылка на версию 8.2!)

1 голос
/ 02 декабря 2011

Как насчет чего-то подобного (если предположить, что искомые значения содержатся в отдельной таблице) ... Хотя я не буду претендовать на хорошую производительность ...

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', '')

поэтому символы установлены в пустую строку.

0 голосов
/ 10 марта 2017

Самый быстрый способ - использовать plperlu, который работает с 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.

...