如何优化组合INNER JOIN,DISTINCT和WHERE的SQL查询?

时间:2018-05-04 01:01:55

标签: sql postgresql postgresql-performance

SELECT DISTINCT options.id, options.foo_option_id, options.description
FROM vehicles 
INNER JOIN vehicle_options     ON vehicle_options.vehicle_id = vehicles.id 
INNER JOIN options             ON options.id = vehicle_options.option_id 
INNER JOIN discounted_vehicles ON vehicles.id = discounted_vehicles.vehicle_id 
WHERE discounted_vehicles.discount_id = 4;

上面的查询返回2067行,并在1.7秒内在本地运行。 我想知道它是否尽可能快,或者我是否能以某种方式进一步调整它,因为这个数据集将会超时快速增长。

我在没有改变速度的情况下尝试过的事情:

1 - 更改连接顺序,从最小表连接到最大表。

2 - 向discounted_vehicles.discount_id添加索引。

4 个答案:

答案 0 :(得分:3)

最佳查询取决于缺少的信息 在典型的设置中,这应该快得多:

SELECT id, foo_option_id, description
FROM   options o
WHERE  EXISTS (
   SELECT
   FROM   discounted_vehicles d
   JOIN   vehicle_options vo USING (vehicle_id)
   WHERE  d.discount_id = 4
   AND    vo.option_id = o.id
   );

假设由FK约束强制执行的引用完整性,我们可以从查询中省略表vehicle并直接从discounted_vehicles加入vehicle_options

此外,如果每个不同选项有许多符合条件的行,EXISTS通常会更快。

理想情况下,您有多列索引:

discounted_vehicles(discount_id, vehicle_id)
vehicle_options(vehicle_id, option_id)

按此顺序索引列。您可能在vehicle_options上有一个PK约束,提供第二个索引,但列顺序应匹配。相关:

根据实际数据分布,可能会有更快的查询样式。相关:

更改加入订单通常是徒劳。 Postgres重新加载以任何预期最快的方式加入。 (例外情况适用。)相关:

答案 1 :(得分:0)

  

1 - 更改连接顺序,从最小表连接到最大表。

幕后花絮PostgreSQL根据SQL优化器设计的解释计划重新排列表的顺序。你写的顺序没有意义。

  

2 - 向discounted_vehicles.discount_id添加索引。

这取决于discount_id列的选择性。你认为它会过滤95%的行只留下5%吗?如果它留下5%或更少,索引将有所帮助。否则全表扫描会更快。

此外,如果还没有,我会添加索引:

vehicle_options (vehicle_id)

但也许它已经由外键创建了。

答案 2 :(得分:0)

尝试使用groupby而不是distinct

SELECT 
    "options"."id",
    "options"."foo_option_id",
    "options"."description"
FROM
    "vehicles" 
    INNER JOIN "vehicle_options" ON "vehicle_options"."vehicle_id" = "vehicles"."id" 
    INNER JOIN "options" ON "options"."id" = "vehicle_options"."option_id" 
    INNER JOIN "discounted_vehicles" ON "vehicles"."id" = "discounted_vehicles"."vehicle_id" 
WHERE 
    "discounted_vehicles"."discount_id" = 4 
GROUP BY 
    "options.id";

答案 3 :(得分:0)

尽管如此,您需要先创建必要的索引,然后尝试在查询

下运行
SELECT "options"."id", "options"."foo_option_id",
    "options"."description"
  FROM "vehicles" 
  INNER JOIN "vehicle_options" 
    ON "vehicle_options"."vehicle_id" = "vehicles"."id" 
  INNER JOIN "options" 
    ON "options"."id" = "vehicle_options"."option_id" 
  INNER JOIN "discounted_vehicles" 
    ON "vehicles"."id" = "discounted_vehicles"."vehicle_id" 
  WHERE "discounted_vehicles"."discount_id" = 4
  GROUP BY options"."id", "options"."foo_option_id",
    "options"."description"