【问题标题】:Postgres - best way to add new rows into tables from a different tablePostgres - 将新行从不同表添加到表中的最佳方法
【发布时间】:2020-04-01 04:11:40
【问题描述】:

场景:我有一个发布数据库,包含以下表格:

  • publication 包含数据库中每本书的信息。
  • keyword 列出所有标识出版物的预定义术语。
  • publication_keyword 是一个匹配出版物和关键字的查找表。
  • new_publication_keyword 是一个查找表,其中包含出版物和关键字之间的新匹配项。

我需要将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');

我想知道当每个表包含数百万行时使用哪种策略最好。目前,我正在使用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

我想将INSERTON CONFLICT 子句一起使用,但要使用它,我需要在publication_keyword 表上创建一个PRIMARY KEY,为这个新索引使用大量磁盘空间:

-- 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;

那么,添加新行的最佳解决方案是什么?

【问题讨论】:

  • 最佳解决方案添加主(或唯一)键。如果这些值应该是唯一的,那么无论如何都应该在数据库中强制执行。

标签: sql postgresql performance merge upsert


【解决方案1】:

您似乎遇到了磁盘空间问题——这有点令人困惑,因为您的表上已经有很多索引。

也就是说,您的解决方案需要三个索引。我只建议两个:

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 使用字符串。整数会更有效。让我猜猜你已经为每张桌子准备了一把钥匙。您可以在数据库中创建一个合成的:

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");

虽然基表更大——因为有一个额外的 4 字节序列 ID 和一个额外的索引,关联表要小得多。

在您的版本中,每行最多 24 + 12 + 1 + 1 = 38 个字节。在这个版本中,每行是 8 个字节。这种差异也会影响索引;另外,固定大小键上的索引通常更有效。

【讨论】:

  • 感谢关于索引的建议,我将创建一个新的PK并删除publication_id索引。我使用的是字符串代码而不是数字代码,因为代码来自外部数据库,我们需要在各种系统中保持相同的代码。
  • @NicolaLepetit 。 . .您应该将代码保留在基表中,并使用数据库内部的序列号作为该数据库中的链接。你浪费了大量的空间来管理字符串——假设平均字符串有 4 个或更多字符。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-12
  • 1970-01-01
  • 1970-01-01
  • 2011-11-11
  • 1970-01-01
  • 2011-11-04
  • 2018-07-30
相关资源
最近更新 更多