加速 SQL 子查询
Speed up SQL subquery
我正在开发一个系统,任何数量的公司都可以将中小企业信息上传到数据库中并进行搜索。
table import_data
存储由不同公司上传的中小企业信息。
各公司上传的信息由tableimport_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);
现在我在做一个推荐系统,其中phone号的记录不存在于特定公司上传的数据中,但存在于另一家公司上传的数据中,推荐给第一家公司购买。
公司与其拥有的所有 import_data 记录之间的关系存储在另一个 table import_data_company_rel
中。 table 包括由特定公司上传和购买的所有 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);
两个 table 都进行了分区,以便每家公司将其数据存储在自己的分区中。
例如,id 为 1 的公司将其数据存储在 import_data_1,import_data_company_rel_1,id 为 2 的公司将其数据存储在 import_data_2 , import_data_company_rel_2 具有与 tables 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;
该查询获取第一页推荐结果,phone条记录未包含在公司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 的公司拥有的 phone 号码,以从推荐中过滤。此查询将逐行扫描整个 table(s)。
我认为分区 table 与慢速查询没有任何关系,因为即使没有分区查询也很慢。
explain (analyze, buffers, format text)
结果可见https://explain.depesz.com/s/MBUL。
我正在研究 postgesql v13.2
如何加快查询速度?有可能吗?任何帮助表示赞赏。谢谢。
您可以做两件事来提高性能:
通过
提高索引扫描的速度
VACUUM import_data_company_rel_1;
这将摆脱昂贵的堆获取。
重写您的查询以使用 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)
我正在开发一个系统,任何数量的公司都可以将中小企业信息上传到数据库中并进行搜索。
table import_data
存储由不同公司上传的中小企业信息。
各公司上传的信息由tableimport_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);
现在我在做一个推荐系统,其中phone号的记录不存在于特定公司上传的数据中,但存在于另一家公司上传的数据中,推荐给第一家公司购买。
公司与其拥有的所有 import_data 记录之间的关系存储在另一个 table import_data_company_rel
中。 table 包括由特定公司上传和购买的所有 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);
两个 table 都进行了分区,以便每家公司将其数据存储在自己的分区中。
例如,id 为 1 的公司将其数据存储在 import_data_1,import_data_company_rel_1,id 为 2 的公司将其数据存储在 import_data_2 , import_data_company_rel_2 具有与 tables 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;
该查询获取第一页推荐结果,phone条记录未包含在公司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 的公司拥有的 phone 号码,以从推荐中过滤。此查询将逐行扫描整个 table(s)。
我认为分区 table 与慢速查询没有任何关系,因为即使没有分区查询也很慢。
explain (analyze, buffers, format text)
结果可见https://explain.depesz.com/s/MBUL。 我正在研究 postgesql v13.2
如何加快查询速度?有可能吗?任何帮助表示赞赏。谢谢。
您可以做两件事来提高性能:
通过
提高索引扫描的速度VACUUM import_data_company_rel_1;
这将摆脱昂贵的堆获取。
重写您的查询以使用
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)