【问题标题】:Speed up SQL subquery加速 SQL 子查询
【发布时间】:2021-06-02 22:27:12
【问题描述】:

我正在开发一个系统,任何数量的公司都可以将中小企业信息上传到数据库并进行搜索。

import_data 表存储了不同公司上传的 SME 信息。 每个公司上传的信息由表import_data的列company_id标识。

CREATE TABLE import_data 
(
    id uuid NOT NULL,    # Indexed
    company_id integer NOT NULL,    # Indexed
    business_name character varying,    # Indexed
    no_of_rating double precision,    # Indexed
    no_of_reviews double precision,    # Indexed
    phone character varying,    # Indexed
    email character varying,    # Indexed
    address character varying,    # Indexed
    upserted_by integer,  # Not indexed
    upsert_date timestamp without time zone,    # Not indexed
)
PARTITION BY LIST (company_id);

现在我正在开发一个推荐系统,在该系统中,电话号码不在特定公司上传的数据中,但在另一家公司上传的数据中存在的记录被推荐给第一个购买的公司。 公司与其拥有的所有 import_data 记录之间的关系存储在另一个表import_data_company_rel 中。此表包含特定公司上传和购买的所有import_data 记录。

CREATE TABLE import_data_company_rel 
(
    record_id uuid NOT NULL,    # Indexed
    company_id integer NOT NULL    # Indexed
)
PARTITION BY LIST (company_id);

两个表都进行了分区,因此每个公司都可以将他们的数据存储在自己的分区中。

例如,id 为 1 的公司会将其数据存储在 import_data_1、import_data_company_rel_1 中,id 为 2 的公司会将其数据存储在 import_data_2、import_data_company_rel_2 中,它们具有相同的表 import_data 和 import_data_company_rel 架构。

这是用于获取特定公司推荐记录的 SQL 查询,假设 id 为 1。

SELECT id,business_name,address
FROM import_data 
INNER JOIN import_data_company_rel idcr ON import_data.id = idcr.record_id
WHERE idcr.company_id != 1 
  AND import_data.phone NOT IN (SELECT import_data.phone 
                                FROM import_data 
                                INNER JOIN import_data_company_rel idcr
                                      ON import_data.id = idcr.record_id 
                                WHERE idcr.company_id = 1)
LIMIT 100 OFFSET 0;

此查询获取首页推荐结果,电话记录不包含在公司1的记录中。

虽然这适用于少量记录,但此查询不能很好地随记录数量扩展。

当记录数以百万计的时候,为了获取100条记录,甚至一个小时后都没有完成执行。

我发现查询中减慢执行速度的部分是子查询

SELECT import_data.phone 
FROM import_data 
INNER JOIN import_data_company_rel idcr
      ON import_data.id = idcr.record_id 
WHERE idcr.company_id = 1

选择 ID 为 1 的公司已经拥有的所有电话号码,从推荐中过滤掉。此查询将逐行扫描整个表。

我认为对表进行分区与慢查询没有任何关系,因为即使没有分区,查询也很慢。

explain (analyze, buffers, format text)

结果可见https://explain.depesz.com/s/MBUL。 我正在开发 postgesql v13.2

如何加快查询速度?甚至可能吗?任何帮助表示赞赏。谢谢。

【问题讨论】:

    标签: sql postgresql performance subquery


    【解决方案1】:

    您可以做两件事来提高性能:

    1. 将仅索引扫描的速度提高

      VACUUM import_data_company_rel_1;
      

      这将摆脱昂贵的堆提取。

    2. 重写您的查询以使用NOT EXISTS

      import_data.phone NOT IN
          (SELECT import_data.phone
           FROM import_data
              INNER JOIN import_data_company_rel idcr
                 ON import_data.id = idcr.record_id
           WHERE idcr.company_id = 1)
      

      可以改写为

      NOT EXISTS
          (SELECT 1
           FROM import_data
              INNER JOIN import_data_company_rel idcr
                 ON import_data.id = idcr.record_id
           WHERE idcr.company_id = 1
             AND import_data.phone = import_data.phone)
      

    【讨论】:

    • 它不存在还是不存在?问 b/c 我没有得到关于 NOT EXISTS 的结果。还有一些关于 EXISTS 的结果。
    • 应该是NOT EXISTS。这两句话的意思是一样的:“这个记录不属于我的财产”和“我没有任何与这个记录相等的东西”。
    • VACUUM 帮助完成执行。我在新数据库中使用 NOT EXISTS 子句进行测试。我不知道我做错了什么,或者加入同一个表 import_data 有什么问题,NOT EXISTS 似乎不起作用。这是我尝试过的INSERT INTO import_data(company_id, id, phone) VALUES (1, 'b9c9d29b-cedf-45d8-afb4-92d67de0e021', '1111111111'), (1, 'd335e5ae-b172-4372-af5a-b26f5715fc9b', '2222222222'); INSERT INTO import_data_company_rel(company_id, record_id) VALUES (1, 'd335e5ae-b172-4372-af5a-b26f5715fc9b'), (1, 'b9c9d29b-cedf-45d8-afb4-92d67de0e021');
    • 现在模拟公司 2 的记录购买。INSERT INTO import_data_company_rel(company_id, record_id) VALUES (2, 'b9c9d29b-cedf-45d8-afb4-92d67de0e021'); 但查询没有结果。然后我在 SO 上找到了这个。 link 有没有办法将查询结果与子查询结果连接起来?
    • 好吧,既然你有一个LIMIT 100,你可能可以留下NOT IN而不重写查询。
    猜你喜欢
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-26
    • 1970-01-01
    • 2015-07-23
    • 2015-06-11
    • 1970-01-01
    相关资源
    最近更新 更多