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

时间:2021-02-15 14:58:00

标签: sql regex postgresql replace string-function

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

这是我的字符串

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=.*?(\||$))', '')

5 个答案:

答案 0 :(得分:5)

答案:

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. 最后,如果您需要消除的参数最后出现(因为参数可以按任意顺序出现),您需要添加额外的替换步骤以消除字符串末尾的残留冒号。< /p>

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

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

答案 1 :(得分:3)

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. 最后将所有这些第一次拆分聚合到一个字符串列表中。

答案 2 :(得分:2)

The S-Man's answer 是一个有效的 ?

<块引用>

当然赞成,解决方案还可以,但它不能完全满足我的问题。因为我希望解决方案在 select 和 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

答案 3 :(得分: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. 该模式使用任何 not |
  3. 字符
  4. 该模式消耗了 |或字符串的结尾

g 标志在 documentation 中被描述为

<块引用>

标志 g 指定替换每个匹配的子字符串,而不仅仅是第一个。

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

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

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

答案 4 :(得分:2)

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

  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