如何从 PostgreSQL 中的文本中删除分隔部分?

How to remove delimited sections from text in PostgreSQL?

我想从字符串中删除一些文本模式,我的字符串有竖线分隔符,参数并不总是相互跟随。

这是我的字符串

TType=SEND|Status=OK|URL=min://j?_a=3&ver=1.1|day=3

我想消除TType=SENDURL=min://j?_a=3&ver=1.1

因此我的最终结果应该是

Status=OK|day=3

我试过的。在 postgresql 中不工作

select REGEXP_REPLACE('TType=SEND|Status=OK|URL=min://j?_a=3&ver=1.1|day=3', 
'(TType=.*?(\||$))|(URL=.*?(\||$))', '')

step-by-step demo:db<>fiddle

SELECT
    string_agg(elements,'|')                                                 -- 3
FROM mytable,
    regexp_split_to_table(mystring, '\|') as elements                        -- 1
WHERE split_part(elements, '=', 1) = ANY(ARRAY['TType', 'URL']) IS NOT TRUE  -- 2
  1. 将字符串拆分为参数,如 A=B。将每个移动到单独的记录中
  2. = 字符处拆分这些元素并过滤没有键 = TTypeURL
  3. 的元素
  4. 最终将所有这些第一次拆分聚合到一个字符串列表中。

是有效的

Sure upvoted, solution is okay however it does not fully satisfy my question. since i would want the solution to be within select and from

如果这是“强制性”要求,那么我会看到以下选项:

  1. 创建函数
  2. 使用 LATERAL JOIN 将所有逻辑封装到一个地方,相关 PostgreSQL: using a calculated column in the same query

最终查询可能如下所示:

SELECT t.*, s.result
FROM t
LEFT JOIN LATERAL (
   SELECT string_agg(elements,'|') AS result
   FROM regexp_split_to_table(t.col, '\|') as elements
   WHERE split_part(elements, '=', 1) = ANY(ARRAY['TType', 'URL']) IS NOT TRUE) s ON TRUE

db<>fiddle demo

或者使用 SELECT 列表中的子查询:

SELECT t.*, 
(
   SELECT string_agg(elements,'|') AS result
   FROM regexp_split_to_table(t.col, '\|') as elements
   WHERE split_part(elements, '=', 1) = ANY(ARRAY['TType', 'URL']) IS NOT TRUE
) AS result
FROM t

db<>fiddle demo 2

以下基于正则表达式的解决方案应该可以解决问题:

SELECT TRIM(REGEXP_REPLACE(
         'TType=SEND|Status=OK|URL=min://j?_a=3&ver=1.1|day=3', 
         '(TType|URL)=[^|]*(\||$)', '', 'g'), '|')
-- outputs:
-- Status=OK|day=3

模式的工作原理:

(TType|URL)=[^|]*(\||$)
|-----------|----|-----
1           2    3
  1. 如果任何子字符串以 TTypeURL 开头,后跟 =
  2. ,则模式开始使用
  3. 模式使用任何不是|
  4. 的字符
  5. 模式消耗 |或字符串结尾

g标志在documentation中被描述为

flag g specifies replacement of each matching substring rather than only the first one.

这里有必要,因为我们要替换所有与我们的模式匹配的子字符串。

最后,有时单个 | 字符可能会保留在字符串的末尾。使用 TRIM

的结果中的任何尾随 | 字符都是 trimmed

您尝试的正则表达式存在一些问题:

  1. 即使使用了非​​贪婪 .*? 匹配,这仍然可以包含管道符号。这可以通过使用允许除管道符号以外的任何内容的匹配器来纠正(这可能是贪婪的):[^|]*
  2. 它应该使用 'g' 标志来替换所有出现的地方,而不仅仅是第一个。
  3. 它只在末尾查找管道,不在开头查找。这意味着如果最后一个管道与最后一个管道之后的字符串匹配(即您的示例中的 URL=...),它将在最后一个管道完好无损。

根据以上几点,这里是一个工作版本:

SELECT REGEXP_REPLACE('TType=SEND|Status=OK|URL=min://j?_a=3&ver=1.1|day=3', '((Status|TType)=[^|]*[|]|[|](Status|TType)=[^|]*)', '', 'g')

Rextester 演示: https://rextester.com/CYBP40923

答案:

SELECT 
REGEXP_REPLACE(
 REGEXP_REPLACE('TType=SEND|Status=OK|URL=min://j?_a=3&ver=1.1|day=3',
  '(TType|URL)=[^|]*\|?', '','g'),
'\|$', '');

解释:

  1. 模式中的 .*? 部分虽然不贪心,但也会消耗冒号,因此不会按预期运行。这是由 [^|]* 修复的,它消耗任何非冒号字符,零次或多次。

  2. 然后您还需要添加全局标志 'g',以替换所有出现的模式,如 documentation.

    中所述
  3. 最后,如果你需要消除的参数出现在最后(因为参数可以以任何顺序出现),你需要添加一个额外的替换步骤来消除最后一个残留的冒号字符串。

例如没有额外的步骤,如下

SELECT
REGEXP_REPLACE('Status=OK|URL=min://j?_a=3&ver=1.1|day=3|TType=SEND',
  '(TType|URL)=[^|]*\|?', '','g');

生产

Status=OK|day=3|

同时,添加额外的步骤,如下

SELECT 
REGEXP_REPLACE(
 REGEXP_REPLACE('Status=OK|URL=min://j?_a=3&ver=1.1|day=3|TType=SEND',
  '(TType|URL)=[^|]*\|?', '','g'),
'\|$', '');

产生所需的

Status=OK|day=3