sql - 为什么这个 DISTINCT/INNER JOIN/ORDER BY postgresql 查询这么慢?

标签 sql performance postgresql

此查询大约需要 4 秒才能完成:

SELECT DISTINCT "resources_resource"."id",
                  "resources_resource"."heading",
                  "resources_resource"."name",
                  "resources_resource"."old_name",
                  "resources_resource"."clean_name",
                  "resources_resource"."sort_name",
                  "resources_resource"."see_also_id",
                  "resources_resource"."referenced_passages",
                  "resources_resource"."resource_type",
                  "resources_resource"."ord",
                  "resources_resource"."content",
                  "resources_resource"."thumb",
                  "resources_resource"."resource_origin"
  FROM "resources_resource"
  INNER JOIN "resources_passageresource" ON ("resources_resource"."id" = "resources_passageresource"."resource_id")
  WHERE "resources_passageresource"."start_ref" >= 66001001
  ORDER BY "resources_resource"."ord" ASC, "resources_resource"."sort_name" ASC LIMIT 5

应大众要求,EXPLAIN ANALYZE:

Limit  (cost=1125.50..1125.68 rows=5 width=803) (actual time=4434.076..4434.557 rows=5 loops=1)
  ->  Unique  (cost=1125.50..1136.91 rows=326 width=803) (actual time=4434.076..4434.557 rows=5 loops=1)
        ->  Sort  (cost=1125.50..1126.32 rows=326 width=803) (actual time=4434.075..4434.075 rows=6 loops=1)
              Sort Key: resources_resource.ord, resources_resource.sort_name, resources_resource.id, resources_resource.heading, resources_resource.name, resources_resource.old_name, resources_resource.clean_name, resources_resource.see_also_id, resources_resource.referenced_passages, resources_resource.resource_type, resources_resource.content, resources_resource.thumb, resources_resource.resource_origin
              Sort Method:  quicksort  Memory: 424kB
              ->  Hash Join  (cost=697.00..1111.89 rows=326 width=803) (actual time=3.453..41.429 rows=424 loops=1)
                    Hash Cond: (resources_passageresource.resource_id = resources_resource.id)
                    ->  Bitmap Heap Scan on resources_passageresource  (cost=10.78..190.19 rows=326 width=4) (actual time=0.107..0.401 rows=424 loops=1)
                          Recheck Cond: (start_ref >= 66001001)
                          ->  Bitmap Index Scan on resources_passageresource_start_ref  (cost=0.00..10.70 rows=326 width=0) (actual time=0.086..0.086 rows=424 loops=1)
                                Index Cond: (start_ref >= 66001001)
                    ->  Hash  (cost=431.32..431.32 rows=2232 width=803) (actual time=3.228..3.228 rows=2232 loops=1)
                          Buckets: 1024  Batches: 2  Memory Usage: 947kB
                          ->  Seq Scan on resources_resource  (cost=0.00..431.32 rows=2232 width=803) (actual time=0.002..1.621 rows=2232 loops=1)
Total runtime: 4435.460 ms

这是 ORM 生成的 SQL。我可以使用 SQL,但我绝对不精通,这里的 EXPLAIN 输出让我很困惑。这个查询是怎么拖我后腿的?

更新:@Ybakos 发现 ORDER_BY 导致了问题。完全删除 ORDER_BY 子句会有所帮助,但查询仍然需要 800 毫秒。这是 EXPLAIN ANALYZE,没有 ORDER_BY:

HashAggregate  (cost=1122.49..1125.75 rows=326 width=803) (actual time=787.519..787.559 rows=104 loops=1)
  ->  Hash Join  (cost=697.00..1111.89 rows=326 width=803) (actual time=3.381..7.312 rows=424 loops=1)
        Hash Cond: (resources_passageresource.resource_id = resources_resource.id)
        ->  Bitmap Heap Scan on resources_passageresource  (cost=10.78..190.19 rows=326 width=4) (actual time=0.095..0.686 rows=424 loops=1)
              Recheck Cond: (start_ref >= 66001001)
              ->  Bitmap Index Scan on resources_passageresource_start_ref  (cost=0.00..10.70 rows=326 width=0) (actual time=0.079..0.079 rows=424 loops=1)
                    Index Cond: (start_ref >= 66001001)
        ->  Hash  (cost=431.32..431.32 rows=2232 width=803) (actual time=3.173..3.173 rows=2232 loops=1)
              Buckets: 1024  Batches: 2  Memory Usage: 947kB
              ->  Seq Scan on resources_resource  (cost=0.00..431.32 rows=2232 width=803) (actual time=0.002..1.568 rows=2232 loops=1)
Total runtime: 787.678 ms

最佳答案

在我看来,DISTINCT 必须用于删除由连接产生的重复项。所以我的问题是,为什么首先要生成重复项?我不完全确定这个由 ORM 生成的查询必须暗示什么,但是如果重写它是一个选项,您当然可以以防止出现重复项的方式重写它。例如,使用 IN:

SELECT "resources_resource"."id",
       "resources_resource"."heading",
       "resources_resource"."name",
       "resources_resource"."old_name",
       "resources_resource"."clean_name",
       "resources_resource"."sort_name",
       "resources_resource"."see_also_id",
       "resources_resource"."referenced_passages",
       "resources_resource"."resource_type",
       "resources_resource"."ord",
       "resources_resource"."content",
       "resources_resource"."thumb",
       "resources_resource"."resource_origin"
  FROM "resources_resource"
  WHERE "resources_resource"."id" IN (
        SELECT "resources_passageresource"."resource_id"
          FROM "resources_passageresource"
          WHERE "resources_passageresource"."start_ref" >= 66001001
        )
  ORDER BY "resources_resource"."ord" ASC, "resources_resource"."sort_name" ASC LIMIT 5

或使用EXISTS:

SELECT "resources_resource"."id",
       "resources_resource"."heading",
       "resources_resource"."name",
       "resources_resource"."old_name",
       "resources_resource"."clean_name",
       "resources_resource"."sort_name",
       "resources_resource"."see_also_id",
       "resources_resource"."referenced_passages",
       "resources_resource"."resource_type",
       "resources_resource"."ord",
       "resources_resource"."content",
       "resources_resource"."thumb",
       "resources_resource"."resource_origin"
  FROM "resources_resource"
  WHERE EXISTS (
        SELECT *
          FROM "resources_passageresource"
          WHERE "resources_passageresource"."resource_id" = "resources_resource"."id"
            AND "resources_passageresource"."start_ref" >= 66001001
        )
  ORDER BY "resources_resource"."ord" ASC, "resources_resource"."sort_name" ASC LIMIT 5

当然,如果完全重写查询是可以接受的,我也会删除列名前面的长表名。例如,考虑以下内容(重写的 IN 查询):

SELECT "id",
       "heading",
       "name",
       "old_name",
       "clean_name",
       "sort_name",
       "see_also_id",
       "referenced_passages",
       "resource_type",
       "ord",
       "content",
       "thumb",
       "resource_origin"
  FROM "resources_resource"
  WHERE "resources_resource"."id" IN (
        SELECT "resource_id"
          FROM "resources_passageresource"
          WHERE "start_ref" >= 66001001
        )
  ORDER BY "ord" ASC, "sort_name" ASC LIMIT 5

关于sql - 为什么这个 DISTINCT/INNER JOIN/ORDER BY postgresql 查询这么慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9917388/

相关文章:

python - Django 与 Heroku 上的 PostgreSQL 应用程序不同步

mysql - 将单个文本列拆分为所有可能组合的两列

php - 如果 DELETE 查询有效,检查 mysql_query 结果?

sql - 选择当年的特定日期

java - 如何处理不同计算机之间的 Java 2D 性能差异?

C#调用native代码比native调用native快

c# - 在没有 SQL Server 的情况下创建 C# 应用程序

performance - 能否消除Elasticsearch结果限制会导致性能问题?

mysql - 将列名映射到字段名 LOAD XML LOCAL INFILE

r - R 或 PostgreSQL 中的最小变异性检查