【问题标题】:I would like to know if there is a better way to write this query (multiple joins of the same table)我想知道是否有更好的方法来编写这个查询(同一张表的多个连接)
【发布时间】:2021-11-04 09:19:59
【问题描述】:

问题来了:

我在db中有vehicles表(这个表的字段不是那么重要),重要的是每辆车都有一个model_id,它引用了vehicle_models表。

车辆模型表具有 id、class、model、series、cm3hp、created_at 和 updated_at 字段。 我需要根据给定标准的库存中有多少特定型号类别的车辆来定义库存年龄。标准是:0-30 天、31-60 天、61-90 天... 360 + 天... 我不知道它是否足够清楚,但让我试着解释得更好:对于每一天的续航里程,我需要找到给定车型类别的车辆数量。还有其他标准,但这对于我试图找出的内容并不重要。为了帮助您更好地理解问题,我将附上结构应该是什么样子的屏幕截图:

我正在使用 MySQL 8。

我写的查询是:

SELECT DISTINCT vm.class,
    IFNULL(t1.count, 0) as t1c,
    IFNULL(t2.count, 0) as t2c,
    IFNULL(t3.count, 0) as t3c,
    IFNULL(t4.count, 0) as t4c,
    IFNULL(t5.count, 0) as t5c,
    IFNULL(t6.count, 0) as t6c,
    IFNULL(t7.count, 0) as t7c
FROM vehicle_models vm
    LEFT JOIN (
        SELECT 
            vm.class as class,
            count(*) as count
        FROM a3s186jg7ffmm0q8.vehicles v
            JOIN vehicle_models vm 
            ON vm.id = v.model_id
        WHERE 
            DATEDIFF(IFNULL(v.retail_date, now()), v.wholesale_date) BETWEEN 0 AND 30
        GROUP BY vm.class
    ) t1 ON t1.class = vm.class
    LEFT JOIN (
        SELECT 
            vm.class as class,
            count(*) as count
        FROM a3s186jg7ffmm0q8.vehicles v
            JOIN vehicle_models vm 
            ON vm.id = v.model_id
        WHERE 
            DATEDIFF(IFNULL(v.retail_date, now()), v.wholesale_date) BETWEEN 31 AND 60
        GROUP BY vm.class
    ) t2 ON t2.class = vm.class
    LEFT JOIN (
        SELECT 
            vm.class as class,
            count(*) as count
        FROM a3s186jg7ffmm0q8.vehicles v
            JOIN vehicle_models vm 
            ON vm.id = v.model_id
        WHERE 
            DATEDIFF(IFNULL(v.retail_date, now()), v.wholesale_date) BETWEEN 61 AND 90
        GROUP BY vm.class
    ) t3 ON t3.class = vm.class
    LEFT JOIN (
        SELECT 
            vm.class as class,
            count(*) as count
        FROM a3s186jg7ffmm0q8.vehicles v
            JOIN vehicle_models vm 
            ON vm.id = v.model_id
        WHERE 
            DATEDIFF(IFNULL(v.retail_date, now()), v.wholesale_date) BETWEEN 91 AND 120
        GROUP BY vm.class
    ) t4 ON t4.class = vm.class
    LEFT JOIN (
        SELECT 
            vm.class as class,
            count(*) as count
        FROM a3s186jg7ffmm0q8.vehicles v
            JOIN vehicle_models vm 
            ON vm.id = v.model_id
        WHERE 
            DATEDIFF(IFNULL(v.retail_date, now()), v.wholesale_date) BETWEEN 121 AND 240
        GROUP BY vm.class
    ) t5 ON t5.class = vm.class
    LEFT JOIN (
        SELECT 
            vm.class as class,
            count(*) as count
        FROM a3s186jg7ffmm0q8.vehicles v
            JOIN vehicle_models vm 
            ON vm.id = v.model_id
        WHERE 
            DATEDIFF(IFNULL(v.retail_date, now()), v.wholesale_date) BETWEEN 241 AND 360
        GROUP BY vm.class
    ) t6 ON t6.class = vm.class
    LEFT JOIN (
        SELECT 
            vm.class as class,
            count(*) as count
        FROM a3s186jg7ffmm0q8.vehicles v
            JOIN vehicle_models vm 
            ON vm.id = v.model_id
        WHERE 
            DATEDIFF(IFNULL(v.retail_date, now()), v.wholesale_date) > 360
        GROUP BY vm.class
    ) t7 ON t7.class = vm.class
ORDER BY vm.class;

现在,这提供了所需的结果,但我想知道是否有更好的方法来编写此查询,以提高性能和代码结构。

【问题讨论】:

  • 加入一次,删除where 条件并使用conditional aggregation 进行计数。
  • 你的答案,我会试一试......

标签: mysql sql query-optimization


【解决方案1】:

我猜您是在提交一份库存老化报告(在有人购买之前,这辆车在经销商的停车场上停留了多长时间)。您可以将年龄范围放在顶级选择中,而不是将每个年龄范围放在单独的子查询中。这将使您的查询更快(子查询有成本)并且更短/更易于阅读。

试试这样的嵌套查询。内部查询返回每辆车一行及其老化号。外部查询聚合它们。

SELECT class,
       COUNT(*) total,
       SUM(age BETWEEN 0 AND 30) t1c,
       SUM(age BETWEEN 31 AND 60) t2c,
       SUM(age BETWEEN 61 AND 90) t3c,
       ... etc ...
  FROM (
      SELECT vm.class, 
             DATEDIFF(IFNULL(v.retail_date, now()), v.wholesale_date) age
        FROM a3s186jg7ffmm0q8.vehicles v
        JOIN vehicle_models vm  ON vm.id = v.model_id
       ) subq
 GROUP BY class
 ORDER BY class;

这个 SUM() 技巧在 MySQL 中有效,因为像 age BETWEEN 0 AND 30 这样的表达式在 true 时具有值 1,在 false 时具有值 0。

【讨论】:

  • 这正是我要找的,伙计... ty...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多