У меня есть таблица в базе данных PostgreSQL, и я пытаюсь извлечь подстроку с определенным шаблоном из строки в результате извлечения определенных данных из столбца jsonb. Интересующая строка извлекается с помощью следующей команды (я включаю все подробности на случай какого-либо побочного эффекта, которого я не заметил):
SELECT (e.content #> '{RefOrder, Ordertxt}')::text
FROM cistec.kis_t_event e
WHERE kat::text = 'MEDI'::text
AND (sender='NEO_FK' OR sender='PAED_IPS') AND dat >= '2023-01-01' AND dat <= '2023-12-31' AND LEFT(patnr, 1) <> 'T'
AND patnr NOT IN ('381227', '381234', '381235', '225328') AND fkey NOT ILIKE '%IF%'
Проблема в том, что я хочу извлечь подстроку с определенным шаблоном с помощью регулярного выражения, но не получаю одинаковый результат в двух случаях, которые для меня должны быть эквивалентны (очевидно, это не так, и мне нужно понять, почему) :
Случай 1. В первом случае я выбираю одно конкретное значение из одной из записей в результате приведенного выше запроса.
SELECT (regexp_matches('1. Res. Fieber, Schmerz: max. 1 Stk max. 4x/24h, min. Intervall 6h rektal; 20- (25) mg/kg/Dosis; NG 10-15mg/kg/Dosis; max. 4x/d; 20 mg/kg', '(\d+(\.\d+)?\s?(g|mg|ucg|mmol)/kg\w*(/(h|d))?)$'))[1]
Результат: 20 mg/kg как и должно быть.
Случай 2. Во втором случае я применяю следующий CTE к соответствующей таблице из БД.
SELECT
(e.content #> '{RefOrder, Ordertxt}')::text,
(regexp_matches((e.content #> '{RefOrder, Ordertxt}')::text, '(\d+(\.\d+)?\s?(g|mg|ucg|mmol)/kg\w*(/(h|d))?)$'))[1]
FROM cistec.kis_t_event e
WHERE kat::text = 'MEDI'::text
AND (sender='NEO_FK' OR sender='PAED_IPS') AND dat >= '2023-01-01' AND dat <= '2023-12-31' AND LEFT(patnr, 1) <> 'T'
AND patnr NOT IN ('381227', '381234', '381235', '225328') AND fkey NOT ILIKE '%IF%'
Результат: empty.
Но еще более странно: если я удалю знак доллара ($) из регулярного выражения, я получу ожидаемый результат в соответствующей строке:
SELECT
(e.content #> '{RefOrder, Ordertxt}')::text,
(regexp_matches((e.content #> '{RefOrder, Ordertxt}')::text, '(\d+(\.\d+)?\s?(g|mg|ucg|mmol)/kg\w*(/(h|d))?)'))[1]
FROM cistec.kis_t_event e
WHERE kat::text = 'MEDI'::text
AND (sender='NEO_FK' OR sender='PAED_IPS') AND dat >= '2023-01-01' AND dat <= '2023-12-31' AND LEFT(patnr, 1) <> 'T'
AND patnr NOT IN ('381227', '381234', '381235', '225328') AND fkey NOT ILIKE '%IF%'
Результат: 15mg/kg.
Но проблема в том, что я хочу получить подстроку, когда она является последней частью строки (поэтому 20 mg/kg не 15mg/kg. Следовательно, использование $ для захвата этой функции.
Знаете, почему такое поведение?
Было бы полезно, если бы вы добавили образец таблицы, поскольку со строками в таблице может быть что-то не так, например, последний пробел. Тогда это будет соответствовать без $, но не с $.
Если возможны символы, не являющиеся словами, используйте (\d+(?:\.\d+)?\s?(?:g|mg|ucg|mmol)/kg\w*(?:/[hd])?)\W*$
@MatsKindahl: в конце строки нет пробела. Строка, которую я взял в случае 1, представляла собой копирование + вставку значения столбца jsonb из одной строки таблицы.
@Eden Помог ли ответ ниже?
Да. Я так понял, что правила ТАК запрещают говорить: "спасибо, работает!"
@eden Да, но вы можете принять ответ как ответ на свой вопрос (есть галочка, которую вы можете нажать). :)





Проблема в том, что оператор #> возвращает значение поля «необработанное», то есть с кавычками, если это строка, и без кавычек, если это какое-то другое значение.
Например, со следующей таблицей и содержимым:
create table sample (metric_id serial, content jsonb);
insert into sample(content) values
('{"one": "first content"}'),
('{"two": "second magic"}'),
('{"three": "whatever"}'),
('{"four": "oops"}');
Вы получаете это значение:
mats=# select metric_id, content #> '{one}' from sample;
metric_id | ?column?
-----------+-----------------
1 | "first content"
2 |
3 |
4 |
(4 rows)
В результате регулярное выражение не сможет соответствовать концу строки:
mats=# select metric_id, content #> '{one}', regexp_matches((content #> '{one}')::text, 'content$') from sample;
metric_id | ?column? | regexp_matches
-----------+----------+----------------
(0 rows)
Если вы добавите кавычку в регулярное выражение, она будет соответствовать ей:
mats=# select metric_id,
mats-# content #> '{one}',
mats-# regexp_matches(content #> '{one}', 'content$') from '{one}')::text, 'content"$') from sample;
metric_id | ?column? | regexp_matches
-----------+-----------------+----------------
1 | "first content" | {"content\""}
(1 row)
Но более разумно использовать оператор #>>, чтобы извлечь его как text и сопоставить его напрямую:
mats=# select metric_id,
mats-# content #>> '{one}',
mats-# regexp_matches(content #>> '{one}', 'content$') from sample;
metric_id | ?column? | regexp_matches
-----------+---------------+----------------
1 | first content | {content}
(1 row)
Попробуйте добавить дополнительный CR:
(\d+(?:\.\d+)?\s?(?:g|mg|ucg|mmol)/kg\w*(?:/[hd])?)\r?$