【问题标题】:Check if records exists in a Postgres table检查 Postgres 表中是否存在记录
【发布时间】:2017-04-10 02:58:22
【问题描述】:

我必须每 20 秒读取一次 CSV。每个 CSV 包含最小值。 500到最大。 60000 行。我必须在 Postgres 表中插入数据,但在此之前我需要检查项目是否已经插入,因为很可能会出现重复项目。检查唯一性的字段也被编入索引。

所以,我分块读取文件并使用 IN 子句获取数据库中已有的项目。

有没有更好的方法呢?

【问题讨论】:

  • 添加正确的唯一索引并只执行插入,忽略重复错误。
  • 在大数据集上会有更好的表现吗?
  • @meagar:您不能只使用基于集合的操作“忽略重复错误”。事务将被回滚,并且什么也做不了。仅当您 INSERT 一行一行地进行时才有意义 - 就性能而言,这将是一个糟糕的主意。

标签: sql performance postgresql csv postgresql-copy


【解决方案1】:

这应该表现良好:

CREATE TEMP TABLE tmp AS SELECT * FROM tbl LIMIT 0 -- copy layout, but no data

COPY tmp FROM '/absolute/path/to/file' FORMAT csv;

INSERT INTO tbl
SELECT tmp.*
FROM   tmp
LEFT   JOIN tbl USING (tbl_id)
WHERE  tbl.tbl_id IS NULL;

DROP TABLE tmp; -- else dropped at end of session automatically

this answer密切相关。

【讨论】:

  • @TheRaaaZ:加入足够多的列以使其独一无二。 所有列(如果需要)。 USING or the NATURAL clause 会派上用场。不过,通常每个表都应该有一个主键。
  • @ervin 我有列数组,如果列数组有 5 列 col1-col5,你能告诉我查询是什么吗?
  • @TheRaaaZ:Start a new question,给出完整的图片。您可以随时链接到这个以供参考。
【解决方案2】:

首先,为了完整起见,我将 Erwin 的代码更改为使用 except

CREATE TEMP TABLE tmp AS SELECT * FROM tbl LIMIT 0 -- copy layout, but no data
COPY tmp FROM '/absolute/path/to/file' FORMAT csv;

INSERT INTO tbl
SELECT tmp.*
FROM   tmp
except
select *
from tbl

DROP TABLE tmp;

然后我决定自己测试一下。我在 9.1 中使用几乎未触及的postgresql.conf 对其进行了测试。目标表包含 1000 万行,源表包含 30000 行。目标表中已存在 15,000 个。

create table tbl (id integer primary key)
;
insert into tbl
select generate_series(1, 10000000)
;
create temp table tmp as select * from tbl limit 0
;
insert into tmp
select generate_series(9985000, 10015000)
;

我只要求解释选择部分。 except 版本:

explain
select *
from tmp
except
select *
from tbl
;
                                       QUERY PLAN                                       
----------------------------------------------------------------------------------------
 HashSetOp Except  (cost=0.00..270098.68 rows=200 width=4)
   ->  Append  (cost=0.00..245018.94 rows=10031897 width=4)
         ->  Subquery Scan on "*SELECT* 1"  (cost=0.00..771.40 rows=31920 width=4)
               ->  Seq Scan on tmp  (cost=0.00..452.20 rows=31920 width=4)
         ->  Subquery Scan on "*SELECT* 2"  (cost=0.00..244247.54 rows=9999977 width=4)
               ->  Seq Scan on tbl  (cost=0.00..144247.77 rows=9999977 width=4)
(6 rows)

outer join 版本:

explain
select *
from 
    tmp
    left join
    tbl using (id)
where tbl.id is null
;
                                QUERY PLAN                                
--------------------------------------------------------------------------
 Nested Loop Anti Join  (cost=0.00..208142.58 rows=15960 width=4)
   ->  Seq Scan on tmp  (cost=0.00..452.20 rows=31920 width=4)
   ->  Index Scan using tbl_pkey on tbl  (cost=0.00..7.80 rows=1 width=4)
         Index Cond: (tmp.id = id)
(4 rows)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-20
    • 2013-05-29
    • 1970-01-01
    • 2015-01-21
    • 2012-08-13
    • 2015-01-02
    相关资源
    最近更新 更多