postgresql 중복 데이터 삭제의 몇 가지 방법 소결

PG 데이터베이스를 사용하는 동안 세 가지 중복 데이터를 삭제하는 방법을 정리했다. 그 중에서 가장 생각나는 것은 가장 일반적인 삭제 방법이지만 이 방법은 성능이 비교적 나쁘고 데이터를 삭제하는 데 시간이 오래 걸리며 실현하기 쉽지만 성능이 너무 나빠서 데이터를 쓰는 속도에 영향을 미친다.
또한 사용된 그룹 by 삭제 방법으로 효율이 높다.
또 하나는 방금 발견된 것으로 아직 검증되지 않았으니 이 세 가지 삭제 방법을 정리하고 각자의 실행 효율을 검증한다.
먼저 기초표를 만들고 일정량의 중복 데이터를 삽입합니다.

  test=# create table deltest(id int, name varchar(255));
  CREATE TABLE
  test=# create table deltest_bk (like deltest);
  CREATE TABLE
  test=# insert into deltest select generate_series(1, 10000), 'ZhangSan';
  INSERT 0 10000
  test=# insert into deltest select generate_series(1, 10000), 'ZhangSan';
  INSERT 0 10000
  test=# insert into deltest_bk select * from deltest;

일반 삭제 방법


가장 쉽게 떠오르는 방법은 데이터가 중복되었는지 판단하는 것이다. 중복된 데이터에 대해ctid의 최소(또는 최대)의 데이터만 보존하고 다른 데이터를 삭제하는 것이다.

test=# explain analyse delete from deltest a where a.ctid <> (select min(t.ctid) from deltest t where a.id=t.id);
                               QUERY PLAN
  -----------------------------------------------------------------------------------------------------------------------------
  Delete on deltest a (cost=0.00..195616.30 rows=1518 width=6) (actual time=67758.866..67758.866 rows=0 loops=1)
    -> Seq Scan on deltest a (cost=0.00..195616.30 rows=1518 width=6) (actual time=32896.517..67663.228 rows=10000 loops=1)
     Filter: (ctid <> (SubPlan 1))
     Rows Removed by Filter: 10000
     SubPlan 1
      -> Aggregate (cost=128.10..128.10 rows=1 width=6) (actual time=3.374..3.374 rows=1 loops=20000)
         -> Seq Scan on deltest t (cost=0.00..128.07 rows=8 width=6) (actual time=0.831..3.344 rows=2 loops=20000)
            Filter: (a.id = id)
            Rows Removed by Filter: 19998
  Total runtime: 67758.931 ms
  test=# select count(*) from deltest;
  count
  -------
  10000
  (1  )
id와 같은 데이터,ctid의 가장 작은 것을 보존하고 다른 삭제를 볼 수 있습니다.deltest표의 데이터를 절반으로 삭제하는 데 67s가 넘는 시간이 소요되는 셈이다.상당히 느리다.

그룹 by 삭제 방법


두 번째 방법은 그룹 by 방법으로 그룹을 통해ctid의 가장 작은 데이터를 찾은 다음 다른 데이터를 삭제합니다.

  test=# truncate table deltest;
  TRUNCATE TABLE
  test=# insert into deltest select * from deltest_bk;
  INSERT 0 20000
  test=# explain analyse delete from deltest a where a.ctid not in (select min(ctid) from deltest group by id);
                               QUERY PLAN
  ----------------------------------------------------------------------------------------------------------------------------------
  Delete on deltest a (cost=131.89..2930.46 rows=763 width=6) (actual time=30942.496..30942.496 rows=0 loops=1)
    -> Seq Scan on deltest a (cost=131.89..2930.46 rows=763 width=6) (actual time=10186.296..30814.366 rows=10000 loops=1)
     Filter: (NOT (SubPlan 1))
     Rows Removed by Filter: 10000
     SubPlan 1
      -> Materialize (cost=131.89..134.89 rows=200 width=10) (actual time=0.001..0.471 rows=7500 loops=20000)
         -> HashAggregate (cost=131.89..133.89 rows=200 width=10) (actual time=10.568..13.584 rows=10000 loops=1)
            -> Seq Scan on deltest (cost=0.00..124.26 rows=1526 width=10) (actual time=0.006..3.829 rows=20000 loops=1)
   Total runtime: 30942.819 ms
  (9  )
  test=# select count(*) from deltest;
   count
  -------
  10000
  (1  )
같은 데이터의 절반을 삭제하고 그룹by를 사용하면 시간을 절반으로 절약할 수 있다.그러나 30s가 필요합니다. 세 번째 삭제 동작을 시도해 보겠습니다.

새로운 삭제 방법


postgres 수련의 길 이 책에서 저자는 효율이 높은 삭제 방법을 언급했는데 여기서 구체적으로 다음과 같이 검증한다.

  test=# truncate table deltest;
  TRUNCATE TABLE
  test=# insert into deltest select * from deltest_bk;
  INSERT 0 20000                             
  test=# explain analyze delete from deltest a where a.ctid = any(array (select ctid from (select row_number() over (partition by id), ctid from deltest) t where t.row_number > 1));
                               QUERY PLAN
  ----------------------------------------------------------------------------------------------------------------------------------
  Delete on deltest a (cost=250.74..270.84 rows=10 width=6) (actual time=98.363..98.363 rows=0 loops=1)
  InitPlan 1 (returns $0)
   -> Subquery Scan on t (cost=204.95..250.73 rows=509 width=6) (actual time=29.446..47.867 rows=10000 loops=1)
      Filter: (t.row_number > 1)
      Rows Removed by Filter: 10000
      -> WindowAgg (cost=204.95..231.66 rows=1526 width=10) (actual time=29.436..44.790 rows=20000 loops=1)
         -> Sort (cost=204.95..208.77 rows=1526 width=10) (actual time=12.466..13.754 rows=20000 loops=1)
            Sort Key: deltest.id
            Sort Method: quicksort Memory: 1294kB
            -> Seq Scan on deltest (cost=0.00..124.26 rows=1526 width=10) (actual time=0.021..5.110 rows=20000 loops=1)
  -> Tid Scan on deltest a (cost=0.01..20.11 rows=10 width=6) (actual time=82.983..88.751 rows=10000 loops=1)
     TID Cond: (ctid = ANY ($0))
  Total runtime: 98.912 ms
  (13  )
  test=# select count(*) from deltest;
  count
  -------
  10000
  (1  )
상술한 결과를 보고 정말 놀랐습니다. 이렇게 빨리 삭제하는 방법은 처음 보았습니다. 자신의 진실하고 견문이 좁습니다. 여기서 수련의 도라는 책의 대신작가에게 경배해야 합니다.
보충: pgsql 삭제표의 중복 데이터 보존 중 하나
1. 테이블(테이블 이름:table 키:id)에 필드rownum을 추가합니다. 형식은 serial입니다.
2. 실행 문장:

delete from table where rownum not in( 
select max(rownum) from table group by id 
)
3. 마지막으로 rownum 삭제
이상의 개인적인 경험으로 여러분께 참고가 되었으면 좋겠습니다. 또한 많은 응원 부탁드립니다.만약 잘못이 있거나 완전한 부분을 고려하지 않으신다면 아낌없이 가르침을 주시기 바랍니다.

좋은 웹페이지 즐겨찾기