【问题标题】:SQL table join many-to-many very slowSQL表连接多对多很慢
【发布时间】:2016-03-15 07:53:10
【问题描述】:

使用 PostgreSQL,我在一个查询中加入了大约 10 个表,它们都是多对多关系。目前数据库仍然很小(总共数百行,所有表合并),但是查询非常慢(1+分钟处理时间)。

由于每次连接都会增加行数(连接 2 个表,每个表有 5 条记录将产生 25 行),结果很快就会变得庞大,超过 300,000 行。查询格式如下:

select * from student_profile sp
  join student_profile_skills sps on sp.id = sps.student_profile_id
  join student_profile_hobby sph on sp.id = sph.student_profile_id
  --and other 8 similar joins
  where sp.id = 1;

这些表很简单(带有 2 个 FK 的联结表)。这里推荐的做法是什么?是必须以更优化的方式编写查询,还是使用单独的查询?提前谢谢!

附加信息:


CREATE TABLE student_profile
(
  id                       serial  NOT NULL,
  first_name               text    NOT NULL,
  last_name                text    NOT NULL,
  country_id               integer,
  city_id                  integer,
  faculty_id               integer,
  university_id            integer,
  degree_id                integer,
  degree_year              integer,
  created_at               timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at               timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT student_profile_pkey               PRIMARY KEY (id),
  CONSTRAINT student_profile_country_id_fkey    FOREIGN KEY (country_id)    REFERENCES country (id),
  CONSTRAINT student_profile_city_id_fkey       FOREIGN KEY (city_id)       REFERENCES city (id),
  CONSTRAINT student_profile_faculty_id_fkey    FOREIGN KEY (faculty_id)    REFERENCES faculty (id),
  CONSTRAINT student_profile_university_id_fkey FOREIGN KEY (university_id) REFERENCES university (id),
  CONSTRAINT student_profile_degree_id_fkey     FOREIGN KEY (degree_id)     REFERENCES degree (id)
);

CREATE TABLE student_profile_skill
(
  id                 serial                   NOT NULL,
  student_profile_id integer                  NOT NULL,
  skill_id           integer                  NOT NULL,
  position           integer                  NOT NULL,
  created_at         timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at         timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT student_profile_skill_pkey                    PRIMARY KEY (id),
  CONSTRAINT student_profile_skill_student_profile_id_fkey FOREIGN KEY (student_profile_id) REFERENCES student_profile (id),
  CONSTRAINT student_profile_skill_skill_id_fkey           FOREIGN KEY (skill_id)           REFERENCES skill (id),
  CONSTRAINT student_profile_skill_unique                  UNIQUE (student_profile_id, skill_id),
  CONSTRAINT student_profile_skill_position_unique         UNIQUE (student_profile_id, position)
);

【问题讨论】:

  • 你能显示一些查询和/或表的详细信息吗?
  • 你需要给我们表格 DDLs
  • 查询的形式为: select * from student_profile sp join student_profile_skills sps on sp.id = sps.student_profile_id join student_profile_hobby sph on sp.id = sph.student_profile_id (以及其他 8 个类似的连接)其中 sp.id = 1;
  • 是否所有连接列(即 id 和 _id 列)都已编入索引?
  • 业务问题是什么?为什么你认为你需要这样的结果集?对于一个学生来说,这就像一个交叉连接。

标签: sql performance postgresql join many-to-many


【解决方案1】:

几乎所有多对多连接的推荐做法是将它们拆分为一对多/多对一。但是,如果您的学生拥有超过 1 种技能/爱好,您有 1 对多表的 3 路连接,这会导致大量重复记录。

3 路加入示例,其中 1 名学生具有 2 种技能和 1 种爱好:

Student     Skill       Hobby  
Smith J.    Linguistic  Fishing  
Smith J.    Profiling   Fishing

我会建议 2 个选项中的 1 个:

  1. 仅在需要时选择您需要的值,(因为我无法想象您需要一个同时具有爱好和技能的表格。例如:爱好和技能的单独视图。
  2. 为临时表创建一个脚本,您可以在其中将所有技能写入一个字段,并将所有爱好写入另一个字段。 (您可能需要使用游标来构建它们,因此请记住您不想经常重新构建它们)。

【讨论】:

  • 感谢您的回复!所以这意味着我应该使用单独的 SQL 查询(例如:select * from student_profile_skill where student_profile_id = 1)并在后端代码(Node.js)中组装数据,而不是加入所有这些表?
  • 是的,除非您对拥有重复记录有非常具体的要求(这不太可能)。如果您将结果放入网站或软件中,您只需一次使用 1 个连接来以特定形式显示结果,或者如果您需要一行来显示您需要的 1 个学生的所有技能/爱好创建另一个表并使用光标在新表中为每个学生创建一个条目,并在表的相应字段中添加您的技能/爱好的文本描述。
猜你喜欢
  • 2020-08-14
  • 1970-01-01
  • 1970-01-01
  • 2020-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-08
  • 1970-01-01
相关资源
最近更新 更多