Postgresql:从巨大的csv文件中插入,收集id并尊重唯一约束

时间:2018-04-15 15:00:44

标签: python django postgresql django-queryset django-postgresql

在postgresql数据库中:

class Persons(models.Model):
    person_name = models.CharField(max_length=10, unique=True)

persons.csv文件,包含100万个名称。

$cat persons.csv
Name-1
Name-2
...
Name-1000000

我想:

  1. 创建尚不存在的名称
  2. 查询数据库并获取csv文件中包含的每个名称的ID。
  3. 我的方法:

    1. 使用COPY命令或实现它的django-postgres-copy应用程序。

      还可以利用新的Postgresql-9.5 + upsert feature

    2. 现在,csv文件中的所有名称也在数据库中。

      我需要从数据库中获取他们的ID - 在内存或其他csv文件中以有效的方式:

      • 使用Q个对象

        list_of_million_q = <iterate csv and append Qs>
        million_names = Names.objects.filter(list_of_million_q)
        

      • 使用__in根据名称列表进行过滤:

        list_of_million_names = <iterate csv and append strings>
        million_names = Names.objects.filter(
            person_name__in=[list_of_million_names]
        )
        

    3. 我不认为任何上述获取ID的方法都是有效的。

      更新

      第三种选择,与this post一致,应该是一个很好的解决方案,它结合了以上所有。

2 个答案:

答案 0 :(得分:0)

类似的东西:

SELECT * FROM persons;

创建一个名称:从数据库中收到的名称中的id字典:

db_dict = {'Harry': 1, 'Bob': 2, ...}

查询字典:

ids = []
for name in list_of_million_names:
    if name in db_dict:
        ids.append(db_dict[name])

这样您就可以使用快速字典索引而不是较慢的if x in list方法。

但真正确切知道的唯一方法是对这3种方法进行基准测试。

答案 1 :(得分:0)

This post介绍如何将RETURNINGON CONFLICT一起使用,以便在插入数据库时​​csv文件的内容,当插入成功时,ID将保存在另一个表中,或当 - 由于唯一约束 - 插入被省略。

我在sqlfiddle中测试了它,我使用的a set up类似于用于COPY命令的命令,它直接从csv文件插入数据库,尊重唯一约束。

架构:

CREATE TABLE IF NOT EXISTS label (
  id serial PRIMARY KEY,
  label_name varchar(200) NOT NULL UNIQUE
  );
INSERT INTO label (label_name) VALUES
  ('Name-1'),
  ('Name-2');

CREATE TABLE IF NOT EXISTS ids (
  id serial PRIMARY KEY,
  label_ids varchar(12) NOT NULL
  );

剧本:

CREATE TEMP TABLE tmp_table
(LIKE label INCLUDING DEFAULTS)
ON COMMIT DROP;

INSERT INTO tmp_table (label_name) VALUES
  ('Name-2'),
  ('Name-3');

WITH ins AS(
  INSERT INTO label 
  SELECT *
  FROM tmp_table
  ON CONFLICT (label_name) DO NOTHING
  RETURNING id
)
INSERT INTO ids (label_ids)
SELECT
  id FROM ins
UNION ALL
SELECT
  l.id FROM tmp_table
JOIN label l USING(label_name);

输出:

SELECT * FROM ids;
SELECT * FROM label;