Есть ли функция PostgreSQL, которая позволяет мне взять большую строку, разделить ее на подстроки, а затем заменить только элементы в этих подстроках? - PullRequest
0 голосов
/ 01 мая 2020

схема: fruit_schema

таблица: fruit_table

.

У меня есть эта строка в ячейке в столбце my_column.

x: y. текст, который меня не интересует

    <Item>
    <Name>First</Name>
    <Subject>x:y. Food: Apple. x:y. </Subject>
    </Item>

    <Item>
    <Name>Second</Name>
    <Subject>x:y. x:y. Food: Apple. Type: Red. x:y. x:y.</Subject>
    </Item>

Я хочу добавить 'Type: Red' к первому элементу. Я уже написал этот запрос:

    DO
    $$
    DECLARE
        oldItem varchar := 'Food: Apple.';
        newItem varchar;
    BEGIN
        newItem  = 'Food: Apple. Type: Red.';

        UPDATE 
            fruit_schema.fruit_table AS profile
        SET 
            my_column = REPLACE(my_column, oldItem, newItem)
        WHERE
            profile.my_column ILIKE '%First%</Item>%';
    END$$;

Однако, это мой вывод:

    <Item>
    <Name>First</Name>
    <Subject>x:y. Food: Apple. Type: Red. x:y. </Subject>
    </Item>

    <Item>
    <Name>Second</Name>
    <Subject>x:y. x:y. Food: Apple. Type: Red. Type: Red. x:y. x:y.</Subject>
    </Item>

Первый элемент обновляется правильно, но второй элемент заменяется дубликатом " Тип: Красный ". Можно ли как-нибудь разделить эту гигантскую нить на первый и второй предметы соответственно? Я хотел бы работать только с первой подстрокой, от <Name>First</Name> до </Item>

Я хотел бы сделать это для каждого другого элемента, над которым я хочу работать. Я хотел бы избежать подсчета количества символов в элементах вручную, если это возможно.

Ответы [ 2 ]

2 голосов
/ 01 мая 2020

Вам нужна функция regexp_replace() с отрицательным прогнозом , например:

# select replace(e'aaa\naaa bbb', 'aaa', 'aaa bbb');
┌─────────────┐
│   replace   │
├─────────────┤
│ aaa bbb    ↵│
│ aaa bbb bbb │
└─────────────┘

# select regexp_replace(e'aaa\naaa bbb', 'aaa(\s+)(?!bbb)', 'aaa bbb\1', 'g');
┌────────────────┐
│ regexp_replace │
├────────────────┤
│ aaa bbb       ↵│
│ aaa bbb        │
└────────────────┘

Использование ваших данных:

# select regexp_replace(
  '<Item><Name>First</Name>
     <Subject>x:y. Food: Apple. x:y. </Subject>
   </Item>
   <Item><Name>Second</Name>
     <Subject>x:y. x:y. Food: Apple. Type: Red. x:y. x:y.</Subject>
   </Item>
   <Item><Name>Third</Name>
     <Subject>x:y. Food: Apple. x:y. </Subject>
   </Item>',
  'Food: Apple\.(\s+)(?!Type: Red\.)',
  'Food: Apple. Type: Red.\1', 'g');
┌─────────────────────────────────────────────────────────────────────┐
│                           regexp_replace                            │
├─────────────────────────────────────────────────────────────────────┤
│ <Item>\n<Name>First</Name>                                         ↵│
│      <Subject>x:y. Food: Apple. Type: Red. x:y. </Subject>         ↵│
│    </Item>                                                         ↵│
│    <Item>\n<Name>Second</Name>                                     ↵│
│      <Subject>x:y. x:y. Food: Apple. Type: Red. x:y. x:y.</Subject>↵│
│    </Item>                                                         ↵│
│    <Item>\n<Name>Third</Name>                                      ↵│
│      <Subject>x:y. Food: Apple. Type: Red. x:y. </Subject>         ↵│
│    </Item>                                                          │
└─────────────────────────────────────────────────────────────────────┘

Обновление: как заменить текст только для определенных элементов:

with t(x) as (values('<Item><Name>First</Name>
     <Subject>x:y. Food: Apple. x:y. </Subject>
   </Item>
   <Item><Name>Second</Name>
     <Subject>x:y. x:y. Food: Apple. Type: Red. x:y. x:y.</Subject>
   </Item>
   <Item><Name>Third</Name>
     <Subject>x:y. Food: Apple. x:y. </Subject>
   </Item>'))
select
    string_agg(
        case
            when xx like any(array['%<Name>Third</Name>%','%<Name>Second</Name>%']) then
                regexp_replace(xx, 'Food: Apple\.(\s+)(?!Type: Red\.)', 'Food: Apple. Type: Red.\1', 'g')
            else xx
        end || '</Item>', e'\n')
from t, unnest(string_to_array(replace(x, e'\n', ''), '</Item>')) as xx
where xx <> '';
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│                                                string_agg                                                 │
├───────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ <Item><Name>First</Name>     <Subject>x:y. Food: Apple. x:y. </Subject>   </Item>                        ↵│
│    <Item><Name>Second</Name>     <Subject>x:y. x:y. Food: Apple. Type: Red. x:y. x:y.</Subject>   </Item>↵│
│    <Item><Name>Third</Name>     <Subject>x:y. Food: Apple. Type: Red. x:y. </Subject>   </Item>           │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────┘
0 голосов
/ 01 мая 2020

Для указанного субъекта c вы можете сделать regexp_replace:

SELECT regexp_replace('<Item>
    <Name>First</Name>
    <Subject>x:y. Food: Apple. x:y. </Subject>
    </Item>

    <Item>
    <Name>Second</Name>
    <Subject>x:y. x:y. Food: Apple. Type: Red. x:y. x:y.</Subject>
    </Item>', 'Food: Apple\. ', 'Food: Apple. Type: Red ', '');

Возможно, вам понадобятся некоторые приспособления, если ваши замены отличаются

...