【问题标题】:Is there a way to reduce size of my SQL query?有没有办法减少我的 SQL 查询的大小?
【发布时间】:2019-09-12 07:49:47
【问题描述】:

我正在尝试在 1 个 SQL 查询中从 Stores、Offers、Products 和 Jobs 表中获取我需要的所有详细信息。

数据库关系:

  • 提供belongs_to Store
  • 提供 has_many 产品
  • 提供 has_many 工作

问题是:我想要针对不同的工作状态进行工作计数。

下面的查询确实有效,但我不喜欢它有多大,并且这部分有多个重复项:WHERE "jobs"."offer_id" = "offers"."id"。有没有办法使这个查询优化并且可能更小?

        SELECT
        "offers".*,
        "stores"."name" AS store_name,
        (
          SELECT COUNT(*)
          FROM "products"
          WHERE "products"."offer_id" = "offers"."id"
        ) AS products_count,
        ( 
          SELECT COUNT(*)
          FROM "jobs"
          WHERE "jobs"."offer_id" = "offers"."id"
        ) AS jobs_count,
        (
          SELECT COUNT(*)
          FROM "jobs"
          WHERE "jobs"."offer_id" = "offers"."id" AND "jobs"."status" = 1
        ) AS jobs_in_progress_count,
        (
          SELECT COUNT(*)
          FROM "jobs"
          WHERE "jobs"."offer_id" = "offers"."id" AND "jobs"."status" = 2
        ) AS jobs_done_count,
        (
          SELECT COUNT(*)
          FROM "jobs"
          WHERE "jobs"."offer_id" = "offers"."id" AND "jobs"."status" = 3
        ) AS jobs_error_count
        FROM "offers"
        LEFT JOIN "stores" ON "stores"."id" = "offers"."store_id"
        GROUP BY "offers"."id", "stores"."name"
        ORDER BY "offers"."created_at" DESC
        SELECT
          "offers".*,
          "stores"."name" AS store_name,
          products.products_count,
          jobs.jobs_count,
          jobs.jobs_in_progress_count,
          jobs.jobs_done_count,
          jobs.jobs_error_count
        FROM "offers" 
        LEFT JOIN "stores" ON "stores"."id" = "offers"."store_id"
        LEFT JOIN (
          SELECT p.offer_id,
            COUNT(*) AS products_count
          FROM "products" p
          GROUP BY p.offer_id
        ) "products" ON "products"."offer_id" = "offers"."id"
        LEFT JOIN (
          SELECT j.offer_id,
            COUNT(*) AS jobs_count,
            COUNT(*) FILTER (WHERE j.status = 1) AS jobs_in_progress_count,
            COUNT(*) FILTER (WHERE j.status = 2) AS jobs_done_count,
            COUNT(*) FILTER (WHERE j.status = 3) AS jobs_error_count
          FROM "jobs" j
          GROUP BY j.offer_id
        ) "jobs" ON "jobs"."offer_id" = "offers"."id"
        GROUP BY "offers"."id", "stores"."name", "products"."products_count", "jobs"."jobs_count", "jobs"."jobs_in_progress_count", "jobs"."jobs_done_count", "jobs"."jobs_error_count"
        ORDER BY "offers"."created_at" DESC

【问题讨论】:

  • 我认为您可以对每个表(产品、工作等)进行左连接并执行SELECT COUNT(products.id) as products_countSELECT COUNT(jobs.id) as jobs_count 等操作
  • 如果我离开联接(产品、工作)查询时间由于某种原因从 5 毫秒变为 50 毫秒。我正在使用 PostgreSQL

标签: sql postgresql


【解决方案1】:

您可以使用条件聚合来计算不同的作业:

SELECT "offers".*,
       "stores"."name" AS store_name,
        (
          SELECT COUNT(*)
          FROM "products"
          WHERE "products"."offer_id" = "offers"."id"
        ) AS products_count,
        count(jobs.offer_id) as jobs_count,
        count(jobs.offer_id) filter (where jobs.status = 1) as jobs_in_progress_count,
        count(jobs.offer_id) filter (where jobs.status = 2) as jobs_done_count,
        count(jobs.offer_id) filter (where jobs.status = 3) as jobs_error_count
FROM "offers"
  LEFT JOIN "stores" ON "stores"."id" = "offers"."store_id"
  LEFT JOIN jobs ON jobs.offer_id = offers.id 
GROUP BY "offers"."id", "stores"."name"
ORDER BY "offers"."created_at" DESC;

先聚合,然后加入结果可能会更快:

SELECT "offers".*,
       "stores"."name" AS store_name,
        (
          SELECT COUNT(*)
          FROM "products"
          WHERE "products"."offer_id" = "offers"."id"
        ) AS products_count,
        jobs.jobs_count,
        jobs.jobs_in_progress_count,
        jobs.jobs_done_count,
        jobs.jobs_error_count
FROM "offers"
  LEFT JOIN "stores" ON "stores"."id" = "offers"."store_id"
  LEFT JOIN (
    SELECT j.offer_id,
          count(*) as jobs_count,
          count(*) filter (where j.status = 1) as jobs_in_progress_count,
          count(*) filter (where j.status = 2) as jobs_done_count,
          count(*) filter (where j.status = 3) as jobs_error_count
    FROM jobs j
    group by j.offer_id
  ) jobs ON jobs.offer_id = offers.id 
ORDER BY "offers"."created_at" DESC;

这也可以用于计数产品:

SELECT "offers".*,
       "stores"."name" AS store_name,
        prod.products_count,
        jobs.jobs_count,
        jobs.jobs_in_progress_count,
        jobs.jobs_done_count,
        jobs.jobs_error_count
FROM "offers"
  LEFT JOIN "stores" ON "stores"."id" = "offers"."store_id"
  LEFT JOIN (
    SELECT j.offer_id,
          count(*) as jobs_count,
          count(*) filter (where j.status = 1) as jobs_in_progress_count,
          count(*) filter (where j.status = 2) as jobs_done_count,
          count(*) filter (where j.status = 3) as jobs_error_count
    FROM jobs j
    group by j.offer_id
  ) jobs ON jobs.offer_id = offers.id 
  LEFT JOIN (
     select p.offer_id, count(*)
     from products p
     group by p.offer_id
  ) prod on prod.offer_id = offers.id
ORDER BY "offers"."created_at" DESC;

【讨论】:

  • 由于某种原因,如果我在 ORDER BY 之前删除分组 GROUP BY "offers"."id", "stores"."name", "products"."products_count", "jobs"."jobs_count", "jobs"."jobs_in_progress_count", "jobs"."jobs_done_count", "jobs"."jobs_error_count",那么它就不起作用。我正在使用 PostgreSQL,为什么我需要对我选择的所有内容进行分组?
  • @totheflo:第一个查询需要它,但其他两个不需要。
  • 我正在使用您提供的第三个查询,但是如果我从 GROUP BY 语句中删除任何内容,PostgreSQL 会抱怨(我正在使用 Rails)
  • @totheflo:不知道。我确信编写的第三个查询不需要主查询中的 GROUP BY。您是否对其进行了更改或添加任何内容?
  • 不,我只是将prod 更改为products 并添加了一些引号。 (我已经编辑了第一篇文章,以显示我正在使用的完整查询)
猜你喜欢
  • 1970-01-01
  • 2021-09-24
  • 1970-01-01
  • 1970-01-01
  • 2022-08-19
  • 2022-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多