Postgresql:从巨大的 csv 文件插入,收集 ID 并遵守唯一约束
Postgresql: Insert from huge csv file, collect the ids and respect unique constraints
在 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
我想:
- 创建尚不存在的名称
- 查询数据库并获取 csv 文件中包含的每个名称的 ID。
我的做法:
使用实现它的COPY
command or the django-postgres-copy应用程序。
同时利用新的 Postgresql-9.5+ upsert feature.
现在,csv 文件中的所有名称也在数据库中。
我需要以一种有效的方式从数据库中获取他们的 ID,无论是在内存中还是在另一个 csv 文件中:
我觉得以上任何一种获取 id 的方法都不是有效的。
更新
还有第三个选项,按照 的思路,它应该是一个很好的解决方案,结合了上述所有内容。
类似于:
SELECT * FROM persons;
根据从数据库收到的名字创建一个 name: 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 种方法进行基准测试。
描述了如何将 RETURNING
与 ON CONFLICT
一起使用,因此在将 csv 文件的内容插入数据库时,id 将保存在另一个 table 中当插入成功时,或者当 - 由于唯一约束 - 插入被省略时。
我已经在 sqlfiddle where I used that resembles the one used for the 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;
在 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
我想:
- 创建尚不存在的名称
- 查询数据库并获取 csv 文件中包含的每个名称的 ID。
我的做法:
使用实现它的
COPY
command or the django-postgres-copy应用程序。同时利用新的 Postgresql-9.5+ upsert feature.
现在,csv 文件中的所有名称也在数据库中。
我需要以一种有效的方式从数据库中获取他们的 ID,无论是在内存中还是在另一个 csv 文件中:
我觉得以上任何一种获取 id 的方法都不是有效的。
更新
还有第三个选项,按照
类似于:
SELECT * FROM persons;
根据从数据库收到的名字创建一个 name: 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 种方法进行基准测试。
RETURNING
与 ON CONFLICT
一起使用,因此在将 csv 文件的内容插入数据库时,id 将保存在另一个 table 中当插入成功时,或者当 - 由于唯一约束 - 插入被省略时。
我已经在 sqlfiddle where I used 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;