查询以使用正则表达式删除“重复的”行

时间:2019-05-20 10:08:57

标签: database postgresql unique-constraint

我正在使用PostgreSQL。我有一张桌子keywords

# Table name: keywords
#
#  id         :integer not null, primary key
#  text       :string  not null
#  match_type :string  not null
#  adgroup_id :integer not null

表具有唯一索引USING btree (match_type, adgroup_id, text)

现在,问题在于,对于相同的adgroup_idmatch_type,有"Hello"" Hello""Hello "" Hello "之类的文本(请注意前导/尾随空格)。 问题是text列在字符串的开头和结尾包含那些空格,从而导致错误的数据(如果没有这些空格,则不会通过uniq索引)。

我计划将来在插入之前添加空白修剪,但是首先我需要清理数据。

如何删除留下重复数据的“重复”数据(基于字符串比较 前导和尾随空格)?

2 个答案:

答案 0 :(得分:1)

这里是使用CTE的一种选择。 CTE查找具有两个两个或更多(match_type, adgroup_id)值的所有text组,这些值与修剪的前导和尾随空白相同。我们还将计算以下内容:

  • cnt-对于每个组,出现“纯”文本版本的次数。纯在这里表示没有前导或尾随空格的文本
  • rn-每个(match_type, adgroup_id)组的任意行号,从值1开始


然后,仅当行出现在重复组中并且不是纯文本版本(cnt > 0)或任意行号大于1时,才删除行。这意味着对于情况"Hello "" Hello",这两个记录之一将被任意删除。但是,如果存在带有"Hello"的第三条“纯”记录,则将保留该记录,并且前两个案例均将被删除。

with cte as (
    select match_type, adgroup_id, trim(text) as text,
        count(case when text = trim(text) then 1 end) as cnt,
        row_number() over (partition by match_type, adgroup_id order by trim(text)) rn
    from keywords
    group by match_type, adgroup_id, trim(text)
    having count(*) > 1
)

delete
from keywords k1
where exists (select 1 from cte k2
              where k1.match_type = k2.match_type and
                    k1.adgroup_id = k2.adgroup_id and
                    k1.text <> k2.text and (k2.cnt > 0 or k2.rn > 1));

答案 1 :(得分:1)

demo:db<>dbfiddle (示例包含两个组:“ Hello”不带空格的元素;“ Bye”不带空格的元素)

DELETE FROM keywords
WHERE id NOT IN (
    SELECT DISTINCT ON (trim(text))                 --1
        id
    FROM
        keywords
    ORDER BY 
        trim(text), 
        text = trim(text) DESC                   --2
)
  1. 按修剪后的文本分组。
  2. 按修剪后的文本排序,如果文本不带空格,则按信息排序。如果有一个元素,则将首先对其排序,并由DISTINCT ON子句采用。如果没有,将采用其他元素

包含其他列的解决方案:

    DELETE FROM keywords
    WHERE id NOT IN (
        SELECT DISTINCT ON (match_type, adgroup_id, trim(text))
            id
        FROM
            keywords
        ORDER BY 
            match_type,
            adgroup_id,
            trim(text), 
            text = trim(text) DESC
    )