Postgres - 从不同的 table 向 table 添加新行的最佳方式
Postgres - best way to add new rows into tables from a different table
场景:我有一个发布数据库,具有以下 tables:
publication
包含数据库中每本书的信息。
keyword
列出标识出版物的所有预定义术语。
publication_keyword
是与出版物和关键字匹配的查找 table。
new_publication_keyword
是一个查找 table,其中包含出版物和关键字之间的新匹配项。
我需要将 new_publication_keyword
中的行添加到 publication_keyword
中,避免添加已经存在的行。
这是在测试模式中创建场景的 DDL
SET search_path = test;
drop table publication_keyword;
drop table publication;
drop table keyword;
drop table new_publication_keyword;
CREATE TABLE publication (
id VARCHAR(24) NOT NULL,
title VARCHAR(1024),
CONSTRAINT publication_pkey PRIMARY KEY(id)
);
CREATE TABLE keyword (
id VARCHAR(12) NOT NULL,
label VARCHAR(180) NOT NULL,
CONSTRAINT keyword_list_pkey PRIMARY KEY(id)
);
CREATE TABLE publication_keyword (
publication_id VARCHAR(24) NOT NULL,
keyword_id VARCHAR(12) NOT NULL
);
CREATE INDEX publication_keyword_code_idx ON publication_keyword
USING btree (keyword_id COLLATE pg_catalog."default");
CREATE INDEX publication_keyword_pubid_idx ON publication_keyword
USING btree (publication_id COLLATE pg_catalog."default");
CREATE TABLE new_publication_keyword (
publication_id VARCHAR(24) NOT NULL,
keyword_id VARCHAR(12) NOT NULL
);
CREATE INDEX new_publication_keyword_code_idx ON new_publication_keyword
USING btree (keyword_id COLLATE pg_catalog."default");
CREATE INDEX new_publication_keyword_pubid_idx ON new_publication_keyword
USING btree (publication_id COLLATE pg_catalog."default");
insert into publication values ('EAN13CODE1001','Title 1');
insert into publication values ('EAN13CODE1002','Title 2');
insert into publication values ('EAN13CODE1003','Title 3');
insert into keyword values ('KWCODE0001','Keyword 1');
insert into keyword values ('KWCODE0002','Keyword 2');
insert into publication_keyword values ('EAN13CODE1001', 'KWCODE0001');
insert into publication_keyword values ('EAN13CODE1002', 'KWCODE0001');
insert into new_publication_keyword values ('EAN13CODE1001', 'KWCODE0001');
insert into new_publication_keyword values ('EAN13CODE1003', 'KWCODE0001');
insert into new_publication_keyword values ('EAN13CODE1003', 'KWCODE0002');
我想知道当每个 table 包含数百万行时使用哪种最佳策略。目前,我正在使用 LEFT OUTER JOIN
查询,排除所有现有行,但这是一个非常慢的解决方案:
insert into publication_keyword (publication_id, keyword_id)
select npw.publication_id, npw.keyword_id from new_publication_keyword npw left outer join
publication_keyword pw on npw.publication_id = pw.publication_id and npw.keyword_id = pw.keyword_id
where pw.publication_id is null
我想将 INSERT
与 ON CONFLICT
子句一起使用,但要使用它,我需要在 publication_keyword table 上创建一个 PRIMARY KEY,为这个新索引使用大量磁盘 space:
-- Not working with the current schema, need to add a PK on publication_keyword
insert into publication_keyword (publication_id, keyword_id)
select publication_id, keyword_id from new_publication_keyword
ON CONFLICT DO NOTHING;
那么,添加新行的最佳解决方案是什么?
您似乎有磁盘 space 问题 -- 这有点令人困惑,因为您的 table 上已经有很多索引。
也就是说,您的解决方案需要三个索引。我只建议两个:
CREATE TABLE new_publication_keyword (
publication_id VARCHAR(24) NOT NULL,
keyword_id VARCHAR(12) NOT NULL,
PRIMARY KEY (publication_id, keyword_id)
);
CREATE INDEX new_publication_keyword_code_idx ON new_publication_keyword
USING btree (keyword_id COLLATE pg_catalog."default");
定义了主键后,publication_id
就不需要单独的索引了。
而且,综上所述,我不明白您为什么要为 ID 使用字符串。整数会更有效率。让我猜想您已经为每个 table 准备了一把钥匙。您可以在数据库中创建一个合成的:
CREATE TABLE publications (
publication_id SERIAL PRIMARY KEY,
my_id VARCHAR(24) NOT NULL,
title VARCHAR(1024),
CONSTRAINT unq_publication UNIQUE (my_id)
);
CREATE TABLE keywords (
keyword_id serial PRIMARY KEY,
my_id VARCHAR(12) NOT NULL,
label VARCHAR(180) NOT NULL,
CONSTRAINT unq_keyword_list UNIQUE (my_id)
);
那么你有:
CREATE TABLE new_publication_keywords (
publication_id INT NOT NULL REFERENCES publications(publication_id),
keyword_id INT NOT NULL REFERENCES keywords(keyword_id),
PRIMARY KEY (publication_id, keyword_id)
);
CREATE INDEX new_publication_keyword_code_idx ON new_publication_keyword
USING btree (keyword_id COLLATE pg_catalog."default");
虽然基础 table 更大——因为有一个额外的 4 字节序列号和一个额外的索引,关联 table 小得多。
在您的版本中,每行最多 24 + 12 + 1 + 1 = 38 个字节。在这个版本中,每行是 8 个字节。这种差异也会影响索引;另外,固定大小键的索引通常更有效。
场景:我有一个发布数据库,具有以下 tables:
publication
包含数据库中每本书的信息。keyword
列出标识出版物的所有预定义术语。publication_keyword
是与出版物和关键字匹配的查找 table。new_publication_keyword
是一个查找 table,其中包含出版物和关键字之间的新匹配项。
我需要将 new_publication_keyword
中的行添加到 publication_keyword
中,避免添加已经存在的行。
这是在测试模式中创建场景的 DDL
SET search_path = test;
drop table publication_keyword;
drop table publication;
drop table keyword;
drop table new_publication_keyword;
CREATE TABLE publication (
id VARCHAR(24) NOT NULL,
title VARCHAR(1024),
CONSTRAINT publication_pkey PRIMARY KEY(id)
);
CREATE TABLE keyword (
id VARCHAR(12) NOT NULL,
label VARCHAR(180) NOT NULL,
CONSTRAINT keyword_list_pkey PRIMARY KEY(id)
);
CREATE TABLE publication_keyword (
publication_id VARCHAR(24) NOT NULL,
keyword_id VARCHAR(12) NOT NULL
);
CREATE INDEX publication_keyword_code_idx ON publication_keyword
USING btree (keyword_id COLLATE pg_catalog."default");
CREATE INDEX publication_keyword_pubid_idx ON publication_keyword
USING btree (publication_id COLLATE pg_catalog."default");
CREATE TABLE new_publication_keyword (
publication_id VARCHAR(24) NOT NULL,
keyword_id VARCHAR(12) NOT NULL
);
CREATE INDEX new_publication_keyword_code_idx ON new_publication_keyword
USING btree (keyword_id COLLATE pg_catalog."default");
CREATE INDEX new_publication_keyword_pubid_idx ON new_publication_keyword
USING btree (publication_id COLLATE pg_catalog."default");
insert into publication values ('EAN13CODE1001','Title 1');
insert into publication values ('EAN13CODE1002','Title 2');
insert into publication values ('EAN13CODE1003','Title 3');
insert into keyword values ('KWCODE0001','Keyword 1');
insert into keyword values ('KWCODE0002','Keyword 2');
insert into publication_keyword values ('EAN13CODE1001', 'KWCODE0001');
insert into publication_keyword values ('EAN13CODE1002', 'KWCODE0001');
insert into new_publication_keyword values ('EAN13CODE1001', 'KWCODE0001');
insert into new_publication_keyword values ('EAN13CODE1003', 'KWCODE0001');
insert into new_publication_keyword values ('EAN13CODE1003', 'KWCODE0002');
我想知道当每个 table 包含数百万行时使用哪种最佳策略。目前,我正在使用 LEFT OUTER JOIN
查询,排除所有现有行,但这是一个非常慢的解决方案:
insert into publication_keyword (publication_id, keyword_id)
select npw.publication_id, npw.keyword_id from new_publication_keyword npw left outer join
publication_keyword pw on npw.publication_id = pw.publication_id and npw.keyword_id = pw.keyword_id
where pw.publication_id is null
我想将 INSERT
与 ON CONFLICT
子句一起使用,但要使用它,我需要在 publication_keyword table 上创建一个 PRIMARY KEY,为这个新索引使用大量磁盘 space:
-- Not working with the current schema, need to add a PK on publication_keyword
insert into publication_keyword (publication_id, keyword_id)
select publication_id, keyword_id from new_publication_keyword
ON CONFLICT DO NOTHING;
那么,添加新行的最佳解决方案是什么?
您似乎有磁盘 space 问题 -- 这有点令人困惑,因为您的 table 上已经有很多索引。
也就是说,您的解决方案需要三个索引。我只建议两个:
CREATE TABLE new_publication_keyword (
publication_id VARCHAR(24) NOT NULL,
keyword_id VARCHAR(12) NOT NULL,
PRIMARY KEY (publication_id, keyword_id)
);
CREATE INDEX new_publication_keyword_code_idx ON new_publication_keyword
USING btree (keyword_id COLLATE pg_catalog."default");
定义了主键后,publication_id
就不需要单独的索引了。
而且,综上所述,我不明白您为什么要为 ID 使用字符串。整数会更有效率。让我猜想您已经为每个 table 准备了一把钥匙。您可以在数据库中创建一个合成的:
CREATE TABLE publications (
publication_id SERIAL PRIMARY KEY,
my_id VARCHAR(24) NOT NULL,
title VARCHAR(1024),
CONSTRAINT unq_publication UNIQUE (my_id)
);
CREATE TABLE keywords (
keyword_id serial PRIMARY KEY,
my_id VARCHAR(12) NOT NULL,
label VARCHAR(180) NOT NULL,
CONSTRAINT unq_keyword_list UNIQUE (my_id)
);
那么你有:
CREATE TABLE new_publication_keywords (
publication_id INT NOT NULL REFERENCES publications(publication_id),
keyword_id INT NOT NULL REFERENCES keywords(keyword_id),
PRIMARY KEY (publication_id, keyword_id)
);
CREATE INDEX new_publication_keyword_code_idx ON new_publication_keyword
USING btree (keyword_id COLLATE pg_catalog."default");
虽然基础 table 更大——因为有一个额外的 4 字节序列号和一个额外的索引,关联 table 小得多。
在您的版本中,每行最多 24 + 12 + 1 + 1 = 38 个字节。在这个版本中,每行是 8 个字节。这种差异也会影响索引;另外,固定大小键的索引通常更有效。