【问题标题】:How to remove delimited sections from text in PostgreSQL?如何从 PostgreSQL 中的文本中删除分隔部分?
【发布时间】:2021-05-18 11:22:33
【问题描述】:

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

这是我的字符串

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

【问题讨论】:

  • @WiktorStribiżew 未能消除其分隔符中的 URL 参数
  • 字符串是动态的,内容并不总是一样,所以TType并不总是出现在开头
  • 啊,好的,我明白了,这些参数是连续的。
  • @WiktorStribiżew 运气好吗?

标签: sql regex postgresql replace string-function


【解决方案1】:

答案:

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 如果我可以在 ..from 之前的 select 语句中使用它
    • 哪一部分? WHERE 还是 regexp_split_to_table?
    • 适用于子查询:dbfiddle.uk/…
    • 您可以使用 FILTER 子句代替 WHERE 子句:dbfiddle.uk/…
    • 解决方案没问题,但我希望有selectfrom 之间的解决方案。即select (the solution here) from table
    【解决方案3】:

    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

    【讨论】:

      【解决方案4】:

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

      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. 该模式使用 |或字符串的结尾

      g 标志在 documentation 中描述为

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

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

      最后,有时单个| 字符可能会保留在字符串的末尾。任何尾随 | 字符都是来自使用 TRIM 的结果中的 trimmed

      【讨论】:

      • 谢谢,(Status|TType|day)=[^|]*(\||$) 我试过了,没问题。是否可以从上面的正则表达式中删除末端的管道。这是结果URL=min://j?_a=3&amp;ver=1.1|
      • @OmariVictorOmosa,请参阅更新的答案。使用TRIM 函数,可以删除结尾的| 字符。
      【解决方案5】:

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

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

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

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

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

      【讨论】:

      • 伟大的解决方案(Status|TType|day)=[^|]*([|]|$) 为什么这会在结果的末尾留下一个管道。
      • 这是因为正则表达式只在每个匹配项的 end 处而不是在开始处查找管道符号(或文本的结尾处)。如果您需要处理这个问题,那么不幸的是,您将需要一些重复,因为您需要在开始或结束时寻找管道,但不能同时寻找:((Status|TType|day)=[^|]*[|]|[|](Status|TType|day)=[^|]*)。 (现在已经相应地编辑了我的答案)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-15
      • 1970-01-01
      • 2023-01-20
      • 1970-01-01
      • 2013-01-09
      • 1970-01-01
      相关资源
      最近更新 更多